Add confirmation on new player registration (#6849)
authorMuhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
Sat, 13 Jan 2018 11:07:16 +0000 (18:07 +0700)
committerLoïc Blot <nerzhul@users.noreply.github.com>
Sat, 13 Jan 2018 11:07:16 +0000 (12:07 +0100)
* Attempt to add registration confirmation

Using SRP auth mechanism, if server sent AUTH_MECHANISM_FIRST_SRP that means the player isn't exist.
Also tell player about the server and chosen username.
Local game has localhost as IP address of the server.
Add RenderingEngine::draw_menu_scene() to draw GUI and clouds background.
aborted -> connection_aborted

* Rewrite information message text

Client::promptConfirmRegister() -> Client::promptConfirmRegistration()

src/client.cpp
src/client.h
src/client/renderingengine.cpp
src/client/renderingengine.h
src/game.cpp
src/gui/CMakeLists.txt
src/gui/guiConfirmRegistration.cpp [new file with mode: 0644]
src/gui/guiConfirmRegistration.h [new file with mode: 0644]
src/network/clientpackethandler.cpp

index e6af5c324589e3052004a42a836cb5191cfc1fa7..90f1c4e013ff92207f5906fe78ec756124dbee21 100644 (file)
@@ -318,6 +318,10 @@ void Client::step(float dtime)
                initial_step = false;
        }
        else if(m_state == LC_Created) {
+               if (m_is_registration_confirmation_state) {
+                       // Waiting confirmation
+                       return;
+               }
                float &counter = m_connection_reinit_timer;
                counter -= dtime;
                if(counter <= 0.0) {
@@ -974,6 +978,18 @@ void Client::sendInit(const std::string &playerName)
        Send(&pkt);
 }
 
+void Client::promptConfirmRegistration(AuthMechanism chosen_auth_mechanism)
+{
+       m_chosen_auth_mech = chosen_auth_mechanism;
+       m_is_registration_confirmation_state = true;
+}
+
+void Client::confirmRegistration()
+{
+       m_is_registration_confirmation_state = false;
+       startAuth(m_chosen_auth_mech);
+}
+
 void Client::startAuth(AuthMechanism chosen_auth_mechanism)
 {
        m_chosen_auth_mech = chosen_auth_mechanism;
index f5929e0553d8b003ba5fa2f058a87b6d9be0aaf6..11f27e9bdab97d1faee30e029d2704c2822f5c76 100644 (file)
@@ -345,6 +345,9 @@ public:
        { return m_proto_ver; }
 
        bool connectedToServer();
+       void confirmRegistration();
+       bool m_is_registration_confirmation_state = false;
+       bool m_simple_singleplayer_mode;
 
        float mediaReceiveProgress();
 
@@ -448,6 +451,7 @@ private:
        static AuthMechanism choseAuthMech(const u32 mechs);
 
        void sendInit(const std::string &playerName);
+       void promptConfirmRegistration(AuthMechanism chosen_auth_mechanism);
        void startAuth(AuthMechanism chosen_auth_mechanism);
        void sendDeletedBlocks(std::vector<v3s16> &blocks);
        void sendGotBlocks(v3s16 block);
index f9da178b9ad90f2eca68824baebadb800646f894..3c48c28a8cbe0e5a71ca1f6c298f7e7ab6ac35ee 100644 (file)
@@ -410,6 +410,26 @@ void RenderingEngine::_draw_load_screen(const std::wstring &text,
        guitext->remove();
 }
 
+/*
+       Draws the menu scene including (optional) cloud background.
+*/
+void RenderingEngine::_draw_menu_scene(gui::IGUIEnvironment *guienv,
+               float dtime, bool clouds)
+{
+       bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
+       if (cloud_menu_background) {
+               g_menuclouds->step(dtime * 3);
+               g_menuclouds->render();
+               get_video_driver()->beginScene(
+                               true, true, video::SColor(255, 140, 186, 250));
+               g_menucloudsmgr->drawAll();
+       } else
+               get_video_driver()->beginScene(true, true, video::SColor(255, 0, 0, 0));
+
+       guienv->drawAll();
+       get_video_driver()->endScene();
+}
+
 std::vector<core::vector3d<u32>> RenderingEngine::getSupportedVideoModes()
 {
        IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
index ac6b6926c5b694f4724a3471c56bf2f4a0a1ede0..8728363d23eb1e13bc36f021f9a04a8d3aeb03ac 100644 (file)
@@ -110,6 +110,12 @@ public:
                                text, guienv, tsrc, dtime, percent, clouds);
        }
 
+       inline static void draw_menu_scene(
+                       gui::IGUIEnvironment *guienv, float dtime, bool clouds)
+       {
+               s_singleton->_draw_menu_scene(guienv, dtime, clouds);
+       }
+
        inline static void draw_scene(video::SColor skycolor, bool show_hud,
                        bool show_minimap, bool draw_wield_tool, bool draw_crosshair)
        {
@@ -138,6 +144,9 @@ private:
                        ITextureSource *tsrc, float dtime = 0, int percent = 0,
                        bool clouds = true);
 
+       void _draw_menu_scene(gui::IGUIEnvironment *guienv, float dtime = 0,
+                       bool clouds = true);
+
        void _draw_scene(video::SColor skycolor, bool show_hud, bool show_minimap,
                        bool draw_wield_tool, bool draw_crosshair);
 
index 1ab360d3cd711f29ba14f2abaeaae18ef704ecd0..32ec6b73e942b3e3ed1523c8bf48b84abf16ff73 100644 (file)
@@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "filesys.h"
 #include "gettext.h"
 #include "gui/guiChatConsole.h"
+#include "gui/guiConfirmRegistration.h"
 #include "gui/guiFormSpecMenu.h"
 #include "gui/guiKeyChangeMenu.h"
 #include "gui/guiPasswordChange.h"
@@ -1302,6 +1303,7 @@ private:
 
        EventManager *eventmgr = nullptr;
        QuicktuneShortcutter *quicktune = nullptr;
+       bool registration_confirmation_shown = false;
 
        std::unique_ptr<GameUI> m_game_ui;
        GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop()
@@ -1900,10 +1902,10 @@ bool Game::initGui()
 
 bool Game::connectToServer(const std::string &playername,
                const std::string &password, std::string *address, u16 port,
-               bool *connect_ok, bool *aborted)
+               bool *connect_ok, bool *connection_aborted)
 {
        *connect_ok = false;    // Let's not be overly optimistic
-       *aborted = false;
+       *connection_aborted = false;
        bool local_server_mode = false;
 
        showOverlayMessage("Resolving address...", 0, 15);
@@ -1946,6 +1948,8 @@ bool Game::connectToServer(const std::string &playername,
        if (!client)
                return false;
 
+       client->m_simple_singleplayer_mode = simple_singleplayer_mode;
+
        infostream << "Connecting to server at ";
        connect_address.print(&infostream);
        infostream << std::endl;
@@ -1985,6 +1989,9 @@ bool Game::connectToServer(const std::string &playername,
                        }
 
                        // Break conditions
+                       if (*connection_aborted)
+                               break;
+
                        if (client->accessDenied()) {
                                *error_message = "Access denied. Reason: "
                                                + client->accessDeniedReason();
@@ -1994,21 +2001,32 @@ bool Game::connectToServer(const std::string &playername,
                        }
 
                        if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) {
-                               *aborted = true;
+                               *connection_aborted = true;
                                infostream << "Connect aborted [Escape]" << std::endl;
                                break;
                        }
 
-                       wait_time += dtime;
-                       // Only time out if we aren't waiting for the server we started
-                       if (!address->empty() && wait_time > 10) {
-                               *error_message = "Connection timed out.";
-                               errorstream << *error_message << std::endl;
-                               break;
-                       }
+                       if (client->m_is_registration_confirmation_state) {
+                               if (registration_confirmation_shown) {
+                                       // Keep drawing the GUI
+                                       RenderingEngine::draw_menu_scene(guienv, dtime, true);
+                               } else {
+                                       registration_confirmation_shown = true;
+                                       (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1,
+                                                  &g_menumgr, client, playername, password, *address, connection_aborted))->drop();
+                               }
+                       } else {
+                               wait_time += dtime;
+                               // Only time out if we aren't waiting for the server we started
+                               if (!address->empty() && wait_time > 10) {
+                                       *error_message = "Connection timed out.";
+                                       errorstream << *error_message << std::endl;
+                                       break;
+                               }
 
-                       // Update status
-                       showOverlayMessage("Connecting to server...", dtime, 20);
+                               // Update status
+                               showOverlayMessage("Connecting to server...", dtime, 20);
+                       }
                }
        } catch (con::PeerNotFoundException &e) {
                // TODO: Should something be done here? At least an info/error
index 067ba09a87abd29e28b9099c0baad74a51d1b905..fa8ab0e6d0fe5231af9779c5f3686ca6b449b88f 100644 (file)
@@ -1,5 +1,6 @@
 set(gui_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiConfirmRegistration.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp
diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp
new file mode 100644 (file)
index 0000000..f45340f
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+Minetest
+Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
+               <muhammadrifqipriyosusanto@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "guiConfirmRegistration.h"
+#include "client.h"
+#include <IGUICheckBox.h>
+#include <IGUIEditBox.h>
+#include <IGUIButton.h>
+#include <IGUIStaticText.h>
+#include <IGUIFont.h>
+
+#include "gettext.h"
+
+// Continuing from guiPasswordChange.cpp
+const int ID_confirmPassword = 262;
+const int ID_confirm = 263;
+const int ID_message = 264;
+const int ID_cancel = 265;
+
+GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env,
+               gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client,
+               const std::string &playername, const std::string &password,
+               const std::string &address, bool *aborted) :
+               GUIModalMenu(env, parent, id, menumgr),
+               m_client(client), m_playername(playername), m_password(password),
+               m_address(address), m_aborted(aborted)
+{
+}
+
+GUIConfirmRegistration::~GUIConfirmRegistration()
+{
+       removeChildren();
+}
+
+void GUIConfirmRegistration::removeChildren()
+{
+       const core::list<gui::IGUIElement *> &children = getChildren();
+       core::list<gui::IGUIElement *> children_copy;
+       for (gui::IGUIElement *i : children)
+               children_copy.push_back(i);
+       for (gui::IGUIElement *i : children_copy)
+               i->remove();
+}
+void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
+{
+       acceptInput();
+       removeChildren();
+
+       /*
+               Calculate new sizes and positions
+       */
+       core::rect<s32> rect(screensize.X / 2 - 600 / 2, screensize.Y / 2 - 300 / 2,
+                       screensize.X / 2 + 600 / 2, screensize.Y / 2 + 300 / 2);
+
+       DesiredRect = rect;
+       recalculateAbsolutePosition(false);
+
+       v2s32 size = rect.getSize();
+       v2s32 topleft_client(0, 0);
+
+       const wchar_t *text;
+
+       /*
+               Add stuff
+       */
+       s32 ypos = 30;
+       {
+               std::string address = m_address;
+               if (address.empty())
+                       address = "localhost";
+               core::rect<s32> rect(0, 0, 540, 90);
+               rect += topleft_client + v2s32(30, ypos);
+               static const std::string info_text_template = strgettext(
+                               "You are about to join this server (%1$s) with the "
+                               "name \"%2$s\" the first time. If you proceed, a "
+                               "new account using your credentials will be created "
+                               "on this server.\n"
+                               "Please type your password once again to confirm "
+                               "account creation or cancel to abort.");
+               char info_text_buf[1024];
+               snprintf(info_text_buf, sizeof(info_text_buf), info_text_template.c_str(),
+                               address.c_str(), m_playername.c_str());
+               Environment->addStaticText(narrow_to_wide_c(info_text_buf), rect, false,
+                               true, this, -1);
+       }
+
+       ypos += 120;
+       {
+               core::rect<s32> rect(0, 0, 540, 30);
+               rect += topleft_client + v2s32(30, ypos);
+               gui::IGUIEditBox *e = Environment->addEditBox(m_pass_confirm.c_str(),
+                               rect, true, this, ID_confirmPassword);
+               e->setPasswordBox(true);
+       }
+
+       ypos += 90;
+       {
+               core::rect<s32> rect(0, 0, 230, 35);
+               rect = rect + v2s32(size.X / 2 - 220, ypos);
+               text = wgettext("Register and Join");
+               Environment->addButton(rect, this, ID_confirm, text);
+               delete[] text;
+       }
+       {
+               core::rect<s32> rect(0, 0, 120, 35);
+               rect = rect + v2s32(size.X / 2 + 70, ypos);
+               text = wgettext("Cancel");
+               Environment->addButton(rect, this, ID_cancel, text);
+               delete[] text;
+       }
+       {
+               core::rect<s32> rect(0, 0, 200, 20);
+               rect += topleft_client + v2s32(30, ypos - 40);
+               text = wgettext("Passwords do not match!");
+               IGUIElement *e = Environment->addStaticText(
+                               text, rect, false, true, this, ID_message);
+               e->setVisible(false);
+               delete[] text;
+       }
+}
+
+void GUIConfirmRegistration::drawMenu()
+{
+       gui::IGUISkin *skin = Environment->getSkin();
+       if (!skin)
+               return;
+       video::IVideoDriver *driver = Environment->getVideoDriver();
+
+       video::SColor bgcolor(140, 0, 0, 0);
+       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
+
+       gui::IGUIElement::draw();
+}
+
+void GUIConfirmRegistration::closeMenu(bool goNext)
+{
+       quitMenu();
+       if (goNext) {
+               m_client->confirmRegistration();
+       } else {
+               *m_aborted = true;
+               infostream << "Connect aborted [Escape]" << std::endl;
+       }
+}
+
+void GUIConfirmRegistration::acceptInput()
+{
+       gui::IGUIElement *e;
+       e = getElementFromId(ID_confirmPassword);
+       if (e)
+               m_pass_confirm = e->getText();
+}
+
+bool GUIConfirmRegistration::processInput()
+{
+       std::wstring m_password_ws = narrow_to_wide(m_password);
+       if (m_password_ws != m_pass_confirm) {
+               gui::IGUIElement *e = getElementFromId(ID_message);
+               if (e)
+                       e->setVisible(true);
+               return false;
+       }
+       return true;
+}
+
+bool GUIConfirmRegistration::OnEvent(const SEvent &event)
+{
+       if (event.EventType == EET_KEY_INPUT_EVENT) {
+               if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) {
+                       closeMenu(false);
+                       return true;
+               }
+               if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
+                       acceptInput();
+                       if (processInput())
+                               closeMenu(true);
+                       return true;
+               }
+       }
+
+       if (event.EventType != EET_GUI_EVENT)
+               return Parent ? Parent->OnEvent(event) : false;
+
+       if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) {
+               if (!canTakeFocus(event.GUIEvent.Element)) {
+                       dstream << "GUIConfirmRegistration: Not allowing focus "
+                                  "change."
+                               << std::endl;
+                       // Returning true disables focus change
+                       return true;
+               }
+       } else if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
+               switch (event.GUIEvent.Caller->getID()) {
+               case ID_confirm:
+                       acceptInput();
+                       if (processInput())
+                               closeMenu(true);
+                       return true;
+               case ID_cancel:
+                       closeMenu(false);
+                       return true;
+               }
+       } else if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
+               switch (event.GUIEvent.Caller->getID()) {
+               case ID_confirmPassword:
+                       acceptInput();
+                       if (processInput())
+                               closeMenu(true);
+                       return true;
+               }
+       }
+
+       return false;
+}
diff --git a/src/gui/guiConfirmRegistration.h b/src/gui/guiConfirmRegistration.h
new file mode 100644 (file)
index 0000000..e14066e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+Minetest
+Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
+               <muhammadrifqipriyosusanto@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+#include <string>
+
+class Client;
+
+class GUIConfirmRegistration : public GUIModalMenu
+{
+public:
+       GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
+                       s32 id, IMenuManager *menumgr, Client *client,
+                       const std::string &playername, const std::string &password,
+                       const std::string &address, bool *aborted);
+       ~GUIConfirmRegistration();
+
+       void removeChildren();
+       /*
+               Remove and re-add (or reposition) stuff
+       */
+       void regenerateGui(v2u32 screensize);
+
+       void drawMenu();
+
+       void closeMenu(bool goNext);
+
+       void acceptInput();
+
+       bool processInput();
+
+       bool OnEvent(const SEvent &event);
+
+private:
+       Client *m_client = nullptr;
+       const std::string &m_playername;
+       const std::string &m_password;
+       const std::string &m_address;
+       bool *m_aborted = nullptr;
+       std::wstring m_pass_confirm = L"";
+};
index 0ec46049e485db54380b8445a2e801fb3cad1092..14aa7b6e6418539c7cb78952ef4819cf453d6a50 100644 (file)
@@ -96,7 +96,12 @@ void Client::handleCommand_Hello(NetworkPacket* pkt)
 
        // Authenticate using that method, or abort if there wasn't any method found
        if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
-               startAuth(chosen_auth_mechanism);
+               if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP
+                               && !m_simple_singleplayer_mode) {
+                       promptConfirmRegistration(chosen_auth_mechanism);
+               } else {
+                       startAuth(chosen_auth_mechanism);
+               }
        } else {
                m_chosen_auth_mech = AUTH_MECHANISM_NONE;
                m_access_denied = true;