Optional reconnect functionality
authorest31 <MTest31@outlook.com>
Fri, 17 Jul 2015 14:40:41 +0000 (16:40 +0200)
committerest31 <MTest31@outlook.com>
Thu, 23 Jul 2015 05:38:13 +0000 (07:38 +0200)
Enable the server to request the client to reconnect.

This can be done with the now extended minetest.request_shutdown([reason], [reconnect]) setting.

25 files changed:
builtin/fstk/ui.lua
doc/lua_api.txt
minetest.conf.example
src/client.cpp
src/client.h
src/client/clientlauncher.cpp
src/client/clientlauncher.h
src/defaultsettings.cpp
src/environment.cpp
src/environment.h
src/game.cpp
src/game.h
src/guiEngine.cpp
src/guiMainMenu.h
src/network/clientpackethandler.cpp
src/network/networkprotocol.h
src/player.cpp
src/player.h
src/script/cpp_api/s_mainmenu.cpp
src/script/cpp_api/s_mainmenu.h
src/script/lua_api/l_mainmenu.cpp
src/script/lua_api/l_server.cpp
src/script/lua_api/l_server.h
src/server.cpp
src/server.h

index 478a78ad54c608b0379bdd16c59d02ff9dd71ac8..de8ae4d2c24fe65900798d52326e141485527af7 100644 (file)
@@ -54,29 +54,42 @@ end
 --------------------------------------------------------------------------------
 --------------------------------------------------------------------------------
 
+local function wordwrap_quickhack(str)
+       local res = ""
+       local ar = str:split("\n")
+       for i = 1, #ar do
+               local text = ar[i]
+               -- Hack to add word wrapping.
+               -- TODO: Add engine support for wrapping in formspecs
+               while #text > 80 do
+                       if res ~= "" then
+                               res = res .. ","
+                       end
+                       res = res .. core.formspec_escape(string.sub(text, 1, 79))
+                       text = string.sub(text, 80, #text)
+               end
+               if res ~= "" then
+                       res = res .. ","
+               end
+               res = res .. core.formspec_escape(text)
+       end
+       return res
+end
+
 --------------------------------------------------------------------------------
 function ui.update()
        local formspec = ""
 
        -- handle errors
-       if gamedata ~= nil and gamedata.errormessage ~= nil then
-               local ar = gamedata.errormessage:split("\n")
-               for i = 1, #ar do
-                       local text = ar[i]
-                       -- Hack to add word wrapping.
-                       -- TODO: Add engine support for wrapping in formspecs
-                       while #text > 80 do
-                               if formspec ~= "" then
-                                       formspec = formspec .. ","
-                               end
-                               formspec = formspec .. core.formspec_escape(string.sub(text, 1, 79))
-                               text = string.sub(text, 80, #text)
-                       end
-                       if formspec ~= "" then
-                               formspec = formspec .. ","
-                       end
-                       formspec = formspec .. core.formspec_escape(text)
-               end
+       if gamedata ~= nil and gamedata.reconnect_requested then
+               formspec = wordwrap_quickhack(gamedata.errormessage or "")
+               formspec = "size[12,5]" ..
+                               "label[0.5,0;" .. fgettext("The server has requested a reconnect:") ..
+                               "]textlist[0.2,0.8;11.5,3.5;;" .. formspec ..
+                               "]button[6,4.6;3,0.5;btn_reconnect_no;" .. fgettext("Main menu") .. "]" ..
+                               "button[3,4.6;3,0.5;btn_reconnect_yes;" .. fgettext("Reconnect") .. "]"
+       elseif gamedata ~= nil and gamedata.errormessage ~= nil then
+               formspec = wordwrap_quickhack(gamedata.errormessage)
                local error_title
                if string.find(gamedata.errormessage, "ModError") then
                        error_title = fgettext("An error occured in a Lua script, such as a mod:")
@@ -128,13 +141,6 @@ end
 
 --------------------------------------------------------------------------------
 function ui.handle_buttons(fields)
-
-       if fields["btn_error_confirm"] then
-               gamedata.errormessage = nil
-               update_menu()
-               return
-       end
-
        for key,value in pairs(ui.childlist) do
 
                local retval = value:handle_buttons(fields)
@@ -168,8 +174,15 @@ end
 --------------------------------------------------------------------------------
 --------------------------------------------------------------------------------
 core.button_handler = function(fields)
-       if fields["btn_error_confirm"] then
+       if fields["btn_reconnect_yes"] then
+               gamedata.reconnect_requested = false
+               gamedata.errormessage = nil
+               gamedata.do_reconnect = true
+               core.start()
+               return
+       elseif fields["btn_reconnect_no"] or fields["btn_error_confirm"] then
                gamedata.errormessage = nil
+               gamedata.reconnect_requested = false
                ui.update()
                return
        end
index b11acb9215e01f15e046ec16f5a6b2c9b8b9de39..75bbbdb07846c0b3aa61b556ed8e93a86e9782c8 100644 (file)
@@ -2174,7 +2174,8 @@ These functions return the leftover itemstack.
     * Optional: Variable number of arguments that are passed to `func`
 
 ### Server
-* `minetest.request_shutdown()`: request for server shutdown
+* `minetest.request_shutdown([message],[reconnect])`: request for server shutdown. Will display `message` to clients,
+    and `reconnect` == true displays a reconnect button.
 * `minetest.get_server_status()`: returns server status string
 
 ### Bans
index 51660e333149b20ad090bb1dde68e0cb296f5268..b9e7d59e4a2359df5b072aa823905eccc41cc078 100644 (file)
 #    A message to be displayed to all clients when the server shuts down
 #kick_msg_crash = This server has experienced an internal error. You will now be disconnected.
 #    A message to be displayed to all clients when the server crashes
+#ask_reconnect_on_crash = false
+#    Whether to ask clients to reconnect after a (lua) crash.
+#    Set this to true if your server is set up to restart automatically.
 
 #    Mod profiler
 #mod_profiling = false
index 1bd8c39aeeaafc97fbef21d46c8803bd709f983b..74072d9d7c3f52cfd49367d181339abcc0ef8347 100644 (file)
@@ -244,6 +244,7 @@ Client::Client(
        m_chosen_auth_mech(AUTH_MECHANISM_NONE),
        m_auth_data(NULL),
        m_access_denied(false),
+       m_access_denied_reconnect(false),
        m_itemdef_received(false),
        m_nodedef_received(false),
        m_media_downloader(new ClientMediaDownloader()),
index 7c1a19eed3086254ba71f5da32f098cecd008b48..547edfeabe48d308617ab1fe54888a1833fb411e 100644 (file)
@@ -489,6 +489,8 @@ public:
        bool accessDenied()
        { return m_access_denied; }
 
+       bool reconnectRequested() { return m_access_denied_reconnect; }
+
        std::string accessDeniedReason()
        { return m_access_denied_reason; }
 
@@ -636,6 +638,7 @@ private:
 
 
        bool m_access_denied;
+       bool m_access_denied_reconnect;
        std::string m_access_denied_reason;
        std::queue<ClientEvent> m_client_event_queue;
        bool m_itemdef_received;
index 50f0690d84677b9b352eb37f265e7040f480d151..bad5c384c3cdbd1c138b1fdd28077489898d6e7f 100644 (file)
@@ -168,8 +168,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()
+       // 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;
 
@@ -197,7 +198,8 @@ 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);
 
                        // If skip_main_menu, we only want to startup once
                        if (skip_main_menu && !first_loop)
@@ -233,6 +235,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,
@@ -245,6 +248,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
                                current_port,
                                error_message,
                                chat_backend,
+                               &reconnect_requested,
                                gamespec,
                                simple_singleplayer_mode
                        );
@@ -325,14 +329,16 @@ bool ClientLauncher::init_engine(int log_level)
 }
 
 bool ClientLauncher::launch_game(std::string &error_message,
-               GameParams &game_params, const Settings &cmd_args)
+               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 = error_message;
+       menudata.address                         = address;
+       menudata.name                            = playername;
+       menudata.port                            = itos(game_params.socket_port);
+       menudata.script_data.errormessage        = error_message;
+       menudata.script_data.reconnect_requested = reconnect_requested;
 
        error_message.clear();
 
@@ -379,11 +385,11 @@ bool ClientLauncher::launch_game(std::string &error_message,
                }
        }
 
-       if (!menudata.errormessage.empty()) {
+       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 = menudata.errormessage;
+               error_message = menudata.script_data.errormessage;
                return false;
        }
 
index 09f8c039a1817be1245eac4173a108a485b6728f..49ceefc52cc67ad630c97516ff9ba6a122aaa8ee 100644 (file)
@@ -92,8 +92,8 @@ protected:
        void init_args(GameParams &game_params, const Settings &cmd_args);
        bool init_engine(int log_level);
 
-       bool launch_game(std::string &error_message, GameParams &game_params,
-                       const Settings &cmd_args);
+       bool launch_game(std::string &error_message, bool reconnect_requested,
+               GameParams &game_params, const Settings &cmd_args);
 
        void main_menu(MainMenuData *menudata);
        bool create_engine_device(int log_level);
index 8df0fbbd806e5e909b90b2cff420e08ec24d62a3..42c6175139f6db0c9beb93e3f34ff9036447bfd2 100644 (file)
@@ -254,6 +254,7 @@ void set_default_settings(Settings *settings)
 
        settings->setDefault("kick_msg_shutdown", "Server shutting down.");
        settings->setDefault("kick_msg_crash", "This server has experienced an internal error. You will now be disconnected.");
+       settings->setDefault("ask_reconnect_on_crash", "false");
 
        settings->setDefault("profiler_print_interval", "0");
        settings->setDefault("enable_mapgen_debug_info", "false");
index d22982b688f789a99f4ad8891943aaedf64037da..5d7321f60a43a2545b596ee33344825be018c0b9 100644 (file)
@@ -426,13 +426,15 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16
        return true;
 }
 
-void ServerEnvironment::kickAllPlayers(const std::string &reason)
+void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
+               const std::string &str_reason, bool reconnect)
 {
-       std::wstring wreason = utf8_to_wide(reason);
        for (std::vector<Player*>::iterator it = m_players.begin();
                        it != m_players.end();
                        ++it) {
-               ((Server*)m_gamedef)->DenyAccess_Legacy((*it)->peer_id, wreason);
+               ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
+                       (*it)->protocol_version, (AccessDeniedCode)reason,
+                       str_reason, reconnect);
        }
 }
 
index 794f1971c8bd94126a4d562f1f0ba7b4565668d1..2bc128f40b71eb997be0120c6b5f8ff403b00aa2 100644 (file)
@@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapnode.h"
 #include "mapblock.h"
 #include "jthread/jmutex.h"
+#include "network/networkprotocol.h" // for AccessDeniedCode
 
 class ServerEnvironment;
 class ActiveBlockModifier;
@@ -221,7 +222,8 @@ public:
        float getSendRecommendedInterval()
                { return m_recommended_send_interval; }
 
-       void kickAllPlayers(const std::string &reason);
+       void kickAllPlayers(AccessDeniedCode reason,
+               const std::string &str_reason, bool reconnect);
        // Save players
        void saveLoadedPlayers();
        void savePlayer(const std::string &playername);
index e3d6b0bc6fb395fe3d627e11dd395e5cbdf9325c..09b8aab42182adc7c0514a1050e4e82787c61487 100644 (file)
@@ -1417,8 +1417,7 @@ struct VolatileRunFlags {
  * hides most of the stuff in this class (nothing in this class is required
  * by any other file) but exposes the public methods/data only.
  */
-class Game
-{
+class Game {
 public:
        Game();
        ~Game();
@@ -1434,6 +1433,7 @@ public:
                        std::string *address,
                        u16 port,
                        std::string &error_message,
+                       bool *reconnect,
                        ChatBackend *chat_backend,
                        const SubgameSpec &gamespec,    // Used for local game
                        bool simple_singleplayer_mode);
@@ -1588,6 +1588,7 @@ private:
        scene::ISceneManager *smgr;
        bool *kill;
        std::string *error_message;
+       bool *reconnect_requested;
        IGameDef *gamedef;                     // Convenience (same as *client)
        scene::ISceneNode *skybox;
 
@@ -1716,17 +1717,19 @@ bool Game::startup(bool *kill,
                std::string *address,     // can change if simple_singleplayer_mode
                u16 port,
                std::string &error_message,
+               bool *reconnect,
                ChatBackend *chat_backend,
                const SubgameSpec &gamespec,
                bool simple_singleplayer_mode)
 {
        // "cache"
-       this->device        = device;
-       this->kill          = kill;
-       this->error_message = &error_message;
-       this->random_input  = random_input;
-       this->input         = input;
-       this->chat_backend  = chat_backend;
+       this->device              = device;
+       this->kill                = kill;
+       this->error_message       = &error_message;
+       this->reconnect_requested = reconnect;
+       this->random_input        = random_input;
+       this->input               = input;
+       this->chat_backend        = chat_backend;
        this->simple_singleplayer_mode = simple_singleplayer_mode;
 
        driver              = device->getVideoDriver();
@@ -2239,6 +2242,7 @@ bool Game::connectToServer(const std::string &playername,
                        if (client->accessDenied()) {
                                *error_message = "Access denied. Reason: "
                                                + client->accessDeniedReason();
+                               *reconnect_requested = client->reconnectRequested();
                                errorstream << *error_message << std::endl;
                                break;
                        }
@@ -2376,6 +2380,7 @@ inline bool Game::checkConnection()
        if (client->accessDenied()) {
                *error_message = "Access denied. Reason: "
                                + client->accessDeniedReason();
+               *reconnect_requested = client->reconnectRequested();
                errorstream << *error_message << std::endl;
                return false;
        }
@@ -4330,6 +4335,7 @@ void the_game(bool *kill,
 
                std::string &error_message,
                ChatBackend &chat_backend,
+               bool *reconnect_requested,
                const SubgameSpec &gamespec,        // Used for local game
                bool simple_singleplayer_mode)
 {
@@ -4344,8 +4350,8 @@ void the_game(bool *kill,
        try {
 
                if (game.startup(kill, random_input, input, device, map_dir,
-                               playername, password, &server_address, port,
-                               error_message, &chat_backend, gamespec,
+                               playername, password, &server_address, port, error_message,
+                               reconnect_requested, &chat_backend, gamespec,
                                simple_singleplayer_mode)) {
                        game.run();
                        game.shutdown();
index 358b26c377eee66413a681f52c547e9ff2108eb1..e1f4e9346a77df11abf2a4f14afe244e0046084b 100644 (file)
@@ -147,6 +147,7 @@ void the_game(bool *kill,
                u16 port,
                std::string &error_message,
                ChatBackend &chat_backend,
+               bool *reconnect_requested,
                const SubgameSpec &gamespec, // Used for local game
                bool simple_singleplayer_mode);
 
index eac9db0c6fceb9908fb2ab200ce2836457bfed0f..c616bc3222883947719500b3c16112759a8a8bf2 100644 (file)
@@ -208,10 +208,8 @@ GUIEngine::GUIEngine(      irr::IrrlichtDevice* dev,
        m_script = new MainMenuScripting(this);
 
        try {
-               if (m_data->errormessage != "") {
-                       m_script->setMainMenuErrorMessage(m_data->errormessage);
-                       m_data->errormessage = "";
-               }
+               m_script->setMainMenuData(&m_data->script_data);
+               m_data->script_data.errormessage = "";
 
                if (!loadMainMenuScript()) {
                        errorstream << "No future without mainmenu" << std::endl;
@@ -219,10 +217,9 @@ GUIEngine::GUIEngine(      irr::IrrlichtDevice* dev,
                }
 
                run();
-       }
-       catch(LuaError &e) {
+       } catch (LuaError &e) {
                errorstream << "MAINMENU ERROR: " << e.what() << std::endl;
-               m_data->errormessage = e.what();
+               m_data->script_data.errormessage = e.what();
        }
 
        m_menu->quitMenu();
index 34362dba6495333422ff91d76e68e0c7092c9947..711ad10f8fd61afaa2bddfce2605c34961093d88 100644 (file)
@@ -25,17 +25,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <string>
 #include <list>
 
-enum
-{
-       TAB_SINGLEPLAYER=0,
-       TAB_MULTIPLAYER,
-       TAB_ADVANCED,
-       TAB_SETTINGS,
-       TAB_CREDITS
+struct MainMenuDataForScript {
+
+       MainMenuDataForScript() :
+               reconnect_requested(false)
+       {}
+
+       // Whether the server has requested a reconnect
+       bool reconnect_requested;
+
+       std::string errormessage;
 };
 
-struct MainMenuData
-{
+struct MainMenuData {
        // Client options
        std::string servername;
        std::string serverdescription;
@@ -43,19 +45,22 @@ struct MainMenuData
        std::string port;
        std::string name;
        std::string password;
+       // Whether to reconnect
+       bool do_reconnect;
 
        // Server options
        bool enable_public;
        int selected_world;
        bool simple_singleplayer_mode;
 
-       //error handling
-       std::string errormessage;
+       // Data to be passed to the script
+       MainMenuDataForScript script_data;
+
        MainMenuData():
+               do_reconnect(false),
                enable_public(false),
                selected_world(0),
-               simple_singleplayer_mode(false),
-               errormessage("")
+               simple_singleplayer_mode(false)
        {}
 };
 
index 15d5456fac11e123faeb476a1bd079199d58c451..2133543d982818a660242351ec604c6928224767 100644 (file)
@@ -215,11 +215,28 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
 
                u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
                *pkt >> denyCode;
-               if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
+               if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
+                               denyCode == SERVER_ACCESSDENIED_CRASH) {
                        *pkt >> m_access_denied_reason;
-               }
-               else if (denyCode < SERVER_ACCESSDENIED_MAX) {
+                       if (m_access_denied_reason == "") {
+                               m_access_denied_reason = accessDeniedStrings[denyCode];
+                       }
+                       u8 reconnect;
+                       *pkt >> reconnect;
+                       m_access_denied_reconnect = reconnect & 1;
+               } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
+                       *pkt >> m_access_denied_reason;
+               } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
                        m_access_denied_reason = accessDeniedStrings[denyCode];
+               } else {
+                       // Allow us to add new error messages to the
+                       // protocol without raising the protocol version, if we want to.
+                       // Until then (which may be never), this is outside
+                       // of the defined protocol.
+                       *pkt >> m_access_denied_reason;
+                       if (m_access_denied_reason == "") {
+                               m_access_denied_reason = "Unknown";
+                       }
                }
        }
        // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
index e4b5667409fbdc4bacb82ab9ef31cc09caa3a2a4..feb18e5098c048aa4db2b2debaf88d27b0ccb980 100644 (file)
@@ -202,7 +202,8 @@ enum ToClientCommand
        TOCLIENT_ACCESS_DENIED = 0x0A,
        /*
                u8 reason
-               std::string custom reason (if reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
+               std::string custom reason (if needed, otherwise "")
+               u8 (bool) reconnect
        */
        TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
        TOCLIENT_ADDNODE = 0x21,
@@ -937,6 +938,8 @@ enum AccessDeniedCode {
        SERVER_ACCESSDENIED_ALREADY_CONNECTED,
        SERVER_ACCESSDENIED_SERVER_FAIL,
        SERVER_ACCESSDENIED_CUSTOM_STRING,
+       SERVER_ACCESSDENIED_SHUTDOWN,
+       SERVER_ACCESSDENIED_CRASH,
        SERVER_ACCESSDENIED_MAX,
 };
 
@@ -954,8 +957,10 @@ const static std::string accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = {
        "Too many users.",
        "Empty passwords are disallowed.  Set a password and try again.",
        "Another client is connected with this name.  If your client closed unexpectedly, try again in a minute.",
-       "Server authention failed.  This is likely a server error."
+       "Server authentication failed.  This is likely a server error.",
        "",
+       "Server shutting down.",
+       "This server has experienced an internal error. You will now be disconnected."
 };
 
 #endif
index 668d490cc4bb10fc9827079ebaeeda6276b5d3b2..0e8fd86d2245c3445ccad7f669cb7aa59acc14ce 100644 (file)
@@ -44,6 +44,7 @@ Player::Player(IGameDef *gamedef, const char *name):
        hp(PLAYER_MAX_HP),
        hurt_tilt_timer(0),
        hurt_tilt_strength(0),
+       protocol_version(0),
        peer_id(PEER_ID_INEXISTENT),
        keyPressed(0),
 // protected
index c84cc1c9290b3db5fe3e6426545c5e12e02a20b6..3a336afc41323f300cc15ea9ce4e11c95319ee51 100644 (file)
@@ -362,6 +362,7 @@ public:
        float hurt_tilt_timer;
        float hurt_tilt_strength;
 
+       u16 protocol_version;
        u16 peer_id;
 
        std::string inventory_formspec;
index 0bb247fa012757f80bae12887062c07aa28e0a2e..7430b0f4f86803aa4777553a3e5d8cb40e70948d 100644 (file)
@@ -21,15 +21,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "cpp_api/s_internal.h"
 #include "common/c_converter.h"
 
-void ScriptApiMainMenu::setMainMenuErrorMessage(std::string errormessage)
+void ScriptApiMainMenu::setMainMenuData(MainMenuDataForScript *data)
 {
        SCRIPTAPI_PRECHECKHEADER
 
        lua_getglobal(L, "gamedata");
        int gamedata_idx = lua_gettop(L);
        lua_pushstring(L, "errormessage");
-       lua_pushstring(L, errormessage.c_str());
+       if (!data->errormessage.empty()) {
+               lua_pushstring(L, data->errormessage.c_str());
+       } else {
+               lua_pushnil(L);
+       }
        lua_settable(L, gamedata_idx);
+       setboolfield(L, gamedata_idx, "reconnect_requested",
+               data->reconnect_requested);
        lua_pop(L, 1);
 }
 
index 6994b578befccb2199e9afe50382235bd2d26ad9..8d5895817e4bd251cf83879c5d1f83fd9d836366 100644 (file)
@@ -22,16 +22,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "cpp_api/s_base.h"
 #include "util/string.h"
+#include "../guiMainMenu.h"
 
-class ScriptApiMainMenu
-               : virtual public ScriptApiBase
-{
+class ScriptApiMainMenu : virtual public ScriptApiBase {
 public:
        /**
-        * set gamedata.errormessage to inform lua of an error
-        * @param errormessage the error message
+        * Hand over MainMenuDataForScript to lua to inform lua of the content
+        * @param data the data
         */
-       void setMainMenuErrorMessage(std::string errormessage);
+       void setMainMenuData(MainMenuDataForScript *data);
 
        /**
         * process events received from formspec
index d209582e9872c480d145b46c47085b9576b42843..92311d6fc1b98c808f9b5d10e99e2d1ec11991ab 100644 (file)
@@ -114,15 +114,19 @@ int ModApiMainMenu::l_start(lua_State *L)
 
        bool valid = false;
 
-
-       engine->m_data->selected_world          = getIntegerData(L, "selected_world",valid) -1;
-       engine->m_data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid);
-       engine->m_data->name                            = getTextData(L,"playername");
-       engine->m_data->password                        = getTextData(L,"password");
-       engine->m_data->address                         = getTextData(L,"address");
-       engine->m_data->port                            = getTextData(L,"port");
-       engine->m_data->serverdescription       = getTextData(L,"serverdescription");
-       engine->m_data->servername                      = getTextData(L,"servername");
+       MainMenuData *data = engine->m_data;
+
+       data->selected_world = getIntegerData(L, "selected_world",valid) -1;
+       data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid);
+       data->do_reconnect = getBoolData(L, "do_reconnect", valid);
+       if (!data->do_reconnect) {
+               data->name     = getTextData(L,"playername");
+               data->password = getTextData(L,"password");
+               data->address  = getTextData(L,"address");
+               data->port     = getTextData(L,"port");
+       }
+       data->serverdescription = getTextData(L,"serverdescription");
+       data->servername        = getTextData(L,"servername");
 
        //close menu next time
        engine->m_startgame = true;
index 558cc0885f83228f867a072c6f9128d1ff8f56f3..96c0327df0e9dff6c3ed529b085c54a27a03c4c7 100644 (file)
@@ -30,7 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 // request_shutdown()
 int ModApiServer::l_request_shutdown(lua_State *L)
 {
-       getServer(L)->requestShutdown();
+       const char *msg = lua_tolstring(L, 1, NULL);
+       bool reconnect = lua_toboolean(L, 2);
+       getServer(L)->requestShutdown(msg ? msg : "", reconnect);
        return 0;
 }
 
index fd85a897505d8006c395cdfedc462faa538040bf..e14bef043a8a1005d065036808894b9f4a413bf8 100644 (file)
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class ModApiServer : public ModApiBase {
 private:
-       // request_shutdown()
+       // request_shutdown([message], [reconnect])
        static int l_request_shutdown(lua_State *L);
 
        // get_server_status()
index 2d6de1c9db0c8d38007cd6acde10e4784e244497..cb7e35ecd5e84b4a3d6fd6baa52a56e5cda6860a 100644 (file)
@@ -191,6 +191,7 @@ Server::Server(
        m_uptime(0),
        m_clients(&m_con),
        m_shutdown_requested(false),
+       m_shutdown_ask_reconnect(false),
        m_ignore_map_edit_events(false),
        m_ignore_map_edit_events_peer_id(0),
        m_next_sound_id(0)
@@ -398,7 +399,17 @@ Server::~Server()
                m_env->saveLoadedPlayers();
 
                infostream << "Server: Kicking players" << std::endl;
-               m_env->kickAllPlayers(g_settings->get("kick_msg_shutdown"));
+               std::string kick_msg;
+               bool reconnect = false;
+               if (getShutdownRequested()) {
+                       reconnect = m_shutdown_ask_reconnect;
+                       kick_msg = m_shutdown_msg;
+               }
+               if (kick_msg == "") {
+                       kick_msg = g_settings->get("kick_msg_shutdown");
+               }
+               m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
+                       kick_msg, reconnect);
 
                infostream << "Server: Saving environment metadata" << std::endl;
                m_env->saveMeta();
@@ -502,7 +513,9 @@ void Server::step(float dtime)
                        throw ServerError(async_err);
                }
                else {
-                       m_env->kickAllPlayers(g_settings->get("kick_msg_crash"));
+                       m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
+                               g_settings->get("kick_msg_crash"),
+                               g_settings->getBool("ask_reconnect_on_crash"));
                        errorstream << "UNRECOVERABLE error occurred. Stopping server. "
                                        << "Please fix the following error:" << std::endl
                                        << async_err << std::endl;
@@ -1070,7 +1083,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
                RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
                if (client != NULL) {
                        playername = client->getName();
-                       playersao = emergePlayer(playername.c_str(), peer_id);
+                       playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
                }
        } catch (std::exception &e) {
                m_clients.Unlock();
@@ -1523,16 +1536,18 @@ void Server::SendBreath(u16 peer_id, u16 breath)
        Send(&pkt);
 }
 
-void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
+void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
+               const std::string &custom_reason, bool reconnect)
 {
-       DSTACK(__FUNCTION_NAME);
+       assert(reason < SERVER_ACCESSDENIED_MAX);
 
        NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
-       pkt << (u8) reason;
-
-       if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING) {
+       pkt << (u8)reason;
+       if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
                pkt << custom_reason;
-       }
+       else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
+                       reason == SERVER_ACCESSDENIED_CRASH)
+               pkt << custom_reason << (u8)reconnect;
        Send(&pkt);
 }
 
@@ -2567,6 +2582,8 @@ void Server::RespawnPlayer(u16 peer_id)
                playersao->setPos(pos);
        }
 }
+
+
 void Server::DenySudoAccess(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
@@ -2575,6 +2592,24 @@ void Server::DenySudoAccess(u16 peer_id)
        Send(&pkt);
 }
 
+
+void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
+               const std::string &str_reason, bool reconnect)
+{
+       if (proto_ver >= 25) {
+               SendAccessDenied(peer_id, reason, str_reason);
+       } else {
+               std::wstring wreason = utf8_to_wide(
+                       reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
+                       accessDeniedStrings[(u8)reason]);
+               SendAccessDenied_Legacy(peer_id, wreason);
+       }
+
+       m_clients.event(peer_id, CSE_SetDenied);
+       m_con.DisconnectPeer(peer_id);
+}
+
+
 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
 {
        DSTACK(__FUNCTION_NAME);
@@ -3330,7 +3365,7 @@ v3f Server::findSpawnPos()
        return intToFloat(nodepos, BS);
 }
 
-PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
+PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
 {
        bool newplayer = false;
 
@@ -3383,6 +3418,8 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
                        getPlayerEffectivePrivs(player->getName()),
                        isSingleplayer());
 
+       player->protocol_version = proto_version;
+
        /* Clean up old HUD elements from previous sessions */
        player->clearHud();
 
index fbca05f265607beb24babfc64a775ccd6aee45a2..5f3905005560a26161ca66bee4420a883fa5a7d8 100644 (file)
@@ -244,8 +244,13 @@ public:
                        { return m_shutdown_requested; }
 
        // request server to shutdown
-       inline void requestShutdown(void)
-                       { m_shutdown_requested = true; }
+       inline void requestShutdown() { m_shutdown_requested = true; }
+       void requestShutdown(const std::string &msg, bool reconnect)
+       {
+               m_shutdown_requested = true;
+               m_shutdown_msg = msg;
+               m_shutdown_ask_reconnect = reconnect;
+       }
 
        // Returns -1 if failed, sound handle on success
        // Envlock
@@ -366,6 +371,8 @@ public:
        void deletingPeer(con::Peer *peer, bool timeout);
 
        void DenySudoAccess(u16 peer_id);
+       void DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
+               const std::string &str_reason = "", bool reconnect = false);
        void DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason="");
        void acceptAuth(u16 peer_id, bool forSudoMode);
        void DenyAccess_Legacy(u16 peer_id, const std::wstring &reason);
@@ -390,7 +397,8 @@ private:
        void SendMovement(u16 peer_id);
        void SendHP(u16 peer_id, u8 hp);
        void SendBreath(u16 peer_id, u16 breath);
-       void SendAccessDenied(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason);
+       void SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
+               const std::string &custom_reason, bool reconnect = false);
        void SendAccessDenied_Legacy(u16 peer_id, const std::wstring &reason);
        void SendDeathscreen(u16 peer_id,bool set_camera_point_target, v3f camera_point_target);
        void SendItemDef(u16 peer_id,IItemDefManager *itemdef, u16 protocol_version);
@@ -490,7 +498,7 @@ private:
 
                Call with env and con locked.
        */
-       PlayerSAO *emergePlayer(const char *name, u16 peer_id);
+       PlayerSAO *emergePlayer(const char *name, u16 peer_id, u16 proto_version);
 
        void handlePeerChanges();
 
@@ -596,6 +604,8 @@ private:
        */
 
        bool m_shutdown_requested;
+       std::string m_shutdown_msg;
+       bool m_shutdown_ask_reconnect;
 
        /*
                Map edit event queue. Automatically receives all map edits.