[CSM] Add `on_dignode` callback (#5140)
[oweals/minetest.git] / src / client / clientlauncher.cpp
index 0f60dcd635aead274b0c21e825aa69055d9b6206..2adac53c2d55d8e79275423abb7596d87da8a4d8 100644 (file)
@@ -17,7 +17,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-#include "main.h"
 #include "mainmenumanager.h"
 #include "debug.h"
 #include "clouds.h"
@@ -33,11 +32,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "guiEngine.h"
 #include "player.h"
 #include "fontengine.h"
+#include "joystick_controller.h"
 #include "clientlauncher.h"
+#include "version.h"
 
-// A pointer to a global instance of the time getter
-// TODO: why?
-TimeGetter *g_timegetter = NULL;
+/* mainmenumanager.h
+ */
+gui::IGUIEnvironment *guienv = NULL;
+gui::IGUIStaticText *guiroot = NULL;
+MainMenuManager g_menumgr;
+
+bool noMenuActive()
+{
+       return g_menumgr.menuCount() == 0;
+}
+
+// Passed to menus to allow disconnecting and exiting
+MainGameCallback *g_gamecallback = NULL;
+
+
+// Instance of the time getter
+static TimeGetter *g_timegetter = NULL;
 
 u32 getTimeMs()
 {
@@ -76,11 +91,14 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
        if (list_video_modes)
                return print_video_modes();
 
-       if (!init_engine(game_params.log_level)) {
+       if (!init_engine()) {
                errorstream << "Could not initialize game engine." << std::endl;
                return false;
        }
 
+       // Create time getter
+       g_timegetter = new IrrlichtTimeGetter(device);
+
        // Speed tests (done after irrlicht is loaded to get timer)
        if (cmd_args.getFlag("speedtests")) {
                dstream << "Running speed tests" << std::endl;
@@ -94,7 +112,9 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
                return false;
        }
 
-       porting::setXorgClassHint(video_driver->getExposedVideoData(), "Minetest");
+       porting::setXorgClassHint(video_driver->getExposedVideoData(), PROJECT_NAME_C);
+
+       porting::setXorgWindowIcon(device);
 
        /*
                This changes the minimum allowed number of vertices in a VBO.
@@ -102,9 +122,6 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
        */
        //driver->setMinHardwareBufferVertexCount(50);
 
-       // Create time getter
-       g_timegetter = new IrrlichtTimeGetter(device);
-
        // Create game callback for menus
        g_gamecallback = new MainGameCallback(device);
 
@@ -128,7 +145,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
        skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255, 255, 255, 255));
 
        g_fontengine = new FontEngine(g_settings, guienv);
-       assert(g_fontengine != NULL);
+       FATAL_ERROR_IF(g_fontengine == NULL, "Font engine creation failed.");
 
 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
        // Irrlicht 1.8 input colours
@@ -155,8 +172,9 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
        ChatBackend chat_backend;
 
        // If an error occurs, this is set to something by menu().
-       // It is then displayed before  the menu shows on the next call to menu()
-       std::wstring error_message = L"";
+       // It is then displayed before the menu shows on the next call to menu()
+       std::string error_message;
+       bool reconnect_requested = false;
 
        bool first_loop = true;
 
@@ -170,7 +188,9 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
        {
                // Set the window caption
                const wchar_t *text = wgettext("Main Menu");
-               device->setWindowCaption((std::wstring(L"Minetest [") + text + L"]").c_str());
+               device->setWindowCaption((utf8_to_wide(PROJECT_NAME_C) +
+                       L" " + utf8_to_wide(g_version_hash) +
+                       L" [" + text + L"]").c_str());
                delete[] text;
 
                try {   // This is used for catching disconnects
@@ -184,7 +204,11 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
                        */
                        guiroot = guienv->addStaticText(L"", core::rect<s32>(0, 0, 10000, 10000));
 
-                       bool game_has_run = launch_game(&error_message, game_params, cmd_args);
+                       bool game_has_run = launch_game(error_message, reconnect_requested,
+                               game_params, cmd_args);
+
+                       // Reset the reconnect_requested flag
+                       reconnect_requested = false;
 
                        // If skip_main_menu, we only want to startup once
                        if (skip_main_menu && !first_loop)
@@ -207,7 +231,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
                        }
 
                        if (current_playername.length() > PLAYERNAME_SIZE-1) {
-                               error_message = wgettext("Player name too long.");
+                               error_message = gettext("Player name too long.");
                                playername = current_playername.substr(0, PLAYERNAME_SIZE-1);
                                g_settings->set("name", playername);
                                continue;
@@ -220,6 +244,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
                        receiver->m_touchscreengui = new TouchScreenGUI(device, receiver);
                        g_touchscreengui = receiver->m_touchscreengui;
 #endif
+
                        the_game(
                                kill,
                                random_input,
@@ -232,6 +257,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
                                current_port,
                                error_message,
                                chat_backend,
+                               &reconnect_requested,
                                gamespec,
                                simple_singleplayer_mode
                        );
@@ -245,25 +271,24 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
 
                } //try
                catch (con::PeerNotFoundException &e) {
-                       error_message = wgettext("Connection error (timed out?)");
-                       errorstream << wide_to_narrow(error_message) << std::endl;
+                       error_message = gettext("Connection error (timed out?)");
+                       errorstream << error_message << std::endl;
                }
 
 #ifdef NDEBUG
                catch (std::exception &e) {
-                       std::string narrow_message = "Some exception: \"";
-                       narrow_message += e.what();
-                       narrow_message += "\"";
-                       errorstream << narrow_message << std::endl;
-                       error_message = narrow_to_wide(narrow_message);
+                       std::string error_message = "Some exception: \"";
+                       error_message += e.what();
+                       error_message += "\"";
+                       errorstream << error_message << std::endl;
                }
 #endif
 
                // If no main menu, show error and exit
                if (skip_main_menu) {
-                       if (error_message != L"") {
+                       if (!error_message.empty()) {
                                verbosestream << "error_message = "
-                                             << wide_to_narrow(error_message) << std::endl;
+                                             << error_message << std::endl;
                                retval = false;
                        }
                        break;
@@ -305,24 +330,27 @@ void ClientLauncher::init_args(GameParams &game_params, const Settings &cmd_args
                        || cmd_args.getFlag("random-input");
 }
 
-bool ClientLauncher::init_engine(int log_level)
+bool ClientLauncher::init_engine()
 {
        receiver = new MyEventReceiver();
-       create_engine_device(log_level);
+       create_engine_device();
        return device != NULL;
 }
 
-bool ClientLauncher::launch_game(std::wstring *error_message,
-               GameParams &game_params, const Settings &cmd_args)
+bool ClientLauncher::launch_game(std::string &error_message,
+               bool reconnect_requested, GameParams &game_params,
+               const Settings &cmd_args)
 {
        // Initialize menu data
        MainMenuData menudata;
-       menudata.address      = address;
-       menudata.name         = playername;
-       menudata.port         = itos(game_params.socket_port);
-       menudata.errormessage = wide_to_narrow(*error_message);
+       menudata.address                         = address;
+       menudata.name                            = playername;
+       menudata.password                        = password;
+       menudata.port                            = itos(game_params.socket_port);
+       menudata.script_data.errormessage        = error_message;
+       menudata.script_data.reconnect_requested = reconnect_requested;
 
-       *error_message = L"";
+       error_message.clear();
 
        if (cmd_args.exists("password"))
                menudata.password = cmd_args.get("password");
@@ -367,22 +395,21 @@ bool ClientLauncher::launch_game(std::wstring *error_message,
                }
        }
 
-       if (menudata.errormessage != "") {
+       if (!menudata.script_data.errormessage.empty()) {
                /* The calling function will pass this back into this function upon the
                 * next iteration (if any) causing it to be displayed by the GUI
                 */
-               *error_message = narrow_to_wide(menudata.errormessage);
+               error_message = menudata.script_data.errormessage;
                return false;
        }
 
-       if (menudata.name == "")
-               menudata.name = std::string("Guest") + itos(myrand_range(1000, 9999));
-       else
-               playername = menudata.name;
-
-       password = translatePassword(playername, narrow_to_wide(menudata.password));
+       if (menudata.name == "" && !simple_singleplayer_mode) {
+               error_message = gettext("Please choose a name!");
+               return false;
+       }
 
-       g_settings->set("name", playername);
+       playername = menudata.name;
+       password = menudata.password;
 
        current_playername = playername;
        current_password   = password;
@@ -396,13 +423,16 @@ bool ClientLauncher::launch_game(std::wstring *error_message,
                current_password = "";
                current_address = "";
                current_port = myrand_range(49152, 65535);
-       } else if (address != "") {
-               ServerListSpec server;
-               server["name"] = menudata.servername;
-               server["address"] = menudata.address;
-               server["port"] = menudata.port;
-               server["description"] = menudata.serverdescription;
-               ServerList::insert(server);
+       } else {
+               g_settings->set("name", playername);
+               if (address != "") {
+                       ServerListSpec server;
+                       server["name"] = menudata.servername;
+                       server["address"] = menudata.address;
+                       server["port"] = menudata.port;
+                       server["description"] = menudata.serverdescription;
+                       ServerList::insert(server);
+               }
        }
 
        infostream << "Selected world: " << worldspec.name
@@ -410,25 +440,25 @@ bool ClientLauncher::launch_game(std::wstring *error_message,
 
        if (current_address == "") { // If local game
                if (worldspec.path == "") {
-                       *error_message = wgettext("No world selected and no address "
+                       error_message = gettext("No world selected and no address "
                                        "provided. Nothing to do.");
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+                       errorstream << error_message << std::endl;
                        return false;
                }
 
                if (!fs::PathExists(worldspec.path)) {
-                       *error_message = wgettext("Provided world path doesn't exist: ")
-                                       + narrow_to_wide(worldspec.path);
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+                       error_message = gettext("Provided world path doesn't exist: ")
+                                       + worldspec.path;
+                       errorstream << error_message << std::endl;
                        return false;
                }
 
                // Load gamespec for required game
                gamespec = findWorldSubgame(worldspec.path);
                if (!gamespec.isValid() && !game_params.game_spec.isValid()) {
-                       *error_message = wgettext("Could not find or load game \"")
-                                       + narrow_to_wide(worldspec.gameid) + L"\"";
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+                       error_message = gettext("Could not find or load game \"")
+                                       + worldspec.gameid + "\"";
+                       errorstream << error_message << std::endl;
                        return false;
                }
 
@@ -437,17 +467,16 @@ bool ClientLauncher::launch_game(std::wstring *error_message,
 
                if (game_params.game_spec.isValid() &&
                                game_params.game_spec.id != worldspec.gameid) {
-                       errorstream << "WARNING: Overriding gamespec from \""
+                       warningstream << "Overriding gamespec from \""
                                    << worldspec.gameid << "\" to \""
                                    << game_params.game_spec.id << "\"" << std::endl;
                        gamespec = game_params.game_spec;
                }
 
                if (!gamespec.isValid()) {
-                       *error_message = wgettext("Invalid gamespec.");
-                       *error_message += L" (world_gameid="
-                                       + narrow_to_wide(worldspec.gameid) + L")";
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+                       error_message = gettext("Invalid gamespec.");
+                       error_message += " (world.gameid=" + worldspec.gameid + ")";
+                       errorstream << error_message << std::endl;
                        return false;
                }
        }
@@ -478,25 +507,14 @@ void ClientLauncher::main_menu(MainMenuData *menudata)
 #endif
 
        /* show main menu */
-       GUIEngine mymenu(device, guiroot, &g_menumgr, smgr, menudata, *kill);
+       GUIEngine mymenu(device, &input->joystick, guiroot,
+               &g_menumgr, smgr, menudata, *kill);
 
        smgr->clear();  /* leave scene manager in a clean state */
 }
 
-bool ClientLauncher::create_engine_device(int log_level)
+bool ClientLauncher::create_engine_device()
 {
-       static const irr::ELOG_LEVEL irr_log_level[5] = {
-               ELL_NONE,
-               ELL_ERROR,
-               ELL_WARNING,
-               ELL_INFORMATION,
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
-               ELL_INFORMATION
-#else
-               ELL_DEBUG
-#endif
-       };
-
        // Resolution selection
        bool fullscreen = g_settings->getBool("fullscreen");
        u16 screenW = g_settings->getU16("screenW");
@@ -507,6 +525,9 @@ bool ClientLauncher::create_engine_device(int log_level)
        u16 bits = g_settings->getU16("fullscreen_bpp");
        u16 fsaa = g_settings->getU16("fsaa");
 
+       // stereo buffer required for pageflip stereo
+       bool stereo_buffer = g_settings->get("3d_mode") == "pageflip";
+
        // Determine driver
        video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
        std::string driverstring = g_settings->get("video_driver");
@@ -532,9 +553,11 @@ bool ClientLauncher::create_engine_device(int log_level)
        params.AntiAlias     = fsaa;
        params.Fullscreen    = fullscreen;
        params.Stencilbuffer = false;
+       params.Stereobuffer  = stereo_buffer;
        params.Vsync         = vsync;
        params.EventReceiver = receiver;
        params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
+       params.ZBufferBits   = 24;
 #ifdef __ANDROID__
        params.PrivateData = porting::app_global;
        params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM +
@@ -544,10 +567,22 @@ bool ClientLauncher::create_engine_device(int log_level)
        device = createDeviceEx(params);
 
        if (device) {
-               // Map our log level to irrlicht engine one.
-               ILogger* irr_logger = device->getLogger();
-               irr_logger->setLogLevel(irr_log_level[log_level]);
-
+               if (g_settings->getBool("enable_joysticks")) {
+                       irr::core::array<irr::SJoystickInfo> infos;
+                       std::vector<irr::SJoystickInfo> joystick_infos;
+                       // Make sure this is called maximum once per
+                       // irrlicht device, otherwise it will give you
+                       // multiple events for the same joystick.
+                       if (device->activateJoysticks(infos)) {
+                               infostream << "Joystick support enabled" << std::endl;
+                               joystick_infos.reserve(infos.size());
+                               for (u32 i = 0; i < infos.size(); i++) {
+                                       joystick_infos.push_back(infos[i]);
+                               }
+                       } else {
+                               errorstream << "Could not activate joystick support." << std::endl;
+                       }
+               }
                porting::initIrrlicht(device);
        }
 
@@ -634,14 +669,14 @@ void ClientLauncher::speed_tests()
                infostream << "Around 5000/ms should do well here." << std::endl;
                TimeTaker timer("Testing mutex speed");
 
-               JMutex m;
+               Mutex m;
                u32 n = 0;
                u32 i = 0;
                do {
                        n += 10000;
                        for (; i < n; i++) {
-                               m.Lock();
-                               m.Unlock();
+                               m.lock();
+                               m.unlock();
                        }
                }
                // Do at least 10ms
@@ -679,7 +714,7 @@ bool ClientLauncher::print_video_modes()
                return false;
        }
 
-       dstream << _("Available video modes (WxHxD):") << std::endl;
+       std::cout << _("Available video modes (WxHxD):") << std::endl;
 
        video::IVideoModeList *videomode_list = nulldevice->getVideoModeList();
 
@@ -690,14 +725,14 @@ bool ClientLauncher::print_video_modes()
                for (s32 i = 0; i < videomode_count; ++i) {
                        videomode_res = videomode_list->getVideoModeResolution(i);
                        videomode_depth = videomode_list->getVideoModeDepth(i);
-                       dstream << videomode_res.Width << "x" << videomode_res.Height
+                       std::cout << videomode_res.Width << "x" << videomode_res.Height
                                << "x" << videomode_depth << std::endl;
                }
 
-               dstream << _("Active video mode (WxHxD):") << std::endl;
+               std::cout << _("Active video mode (WxHxD):") << std::endl;
                videomode_res = videomode_list->getDesktopResolution();
                videomode_depth = videomode_list->getDesktopDepth();
-               dstream << videomode_res.Width << "x" << videomode_res.Height
+               std::cout << videomode_res.Width << "x" << videomode_res.Height
                        << "x" << videomode_depth << std::endl;
 
        }