diff --git a/modules/game/game.lua b/modules/game/game.lua index d9aecd0d..5b2219e9 100644 --- a/modules/game/game.lua +++ b/modules/game/game.lua @@ -1,3 +1,6 @@ +-- private variables +local showCharacterListOnLogout = true + -- private functions local function onGameKeyPress(self, keyCode, keyChar, keyboardModifiers) if keyboardModifiers == KeyboardCtrlModifier then @@ -22,14 +25,28 @@ local function destroyMainInterface() end end +-- public functions function Game.onLogin() MainMenu.hide() CharacterList.destroyLoadBox() createMainInterface() end +function Game.onConnectionError(message) + CharacterList.destroyLoadBox() + local errorBox = displayErrorBox("Login Error", "Connection error: " .. message) + errorBox.onOk = CharacterList.show + showCharacterListOnLogout = false +end + function Game.onLogout() MainMenu.show() - CharacterList.show() + + if showCharacterListOnLogout then + CharacterList.show() + else + showCharacterListOnLogout = true + end + destroyMainInterface() end diff --git a/modules/mainmenu/characterlist.lua b/modules/mainmenu/characterlist.lua index 6547739a..87975053 100644 --- a/modules/mainmenu/characterlist.lua +++ b/modules/mainmenu/characterlist.lua @@ -17,6 +17,27 @@ local function onCharactersWindowKeyPress(self, keyCode, keyChar, keyboardModifi return false end +local function tryLogin(charInfo) + CharacterList.hide() + + if Game.isOnline() then + Game.logout() + scheduleEvent(function() tryLogin(charInfo) end, 250) + return + end + + Game.loginWorld(EnterGame.account, EnterGame.password, charInfo.worldHost, charInfo.worldPort, charInfo.characterName) + + loadBox = displayCancelBox('Please wait', 'Connecting to game server...') + function loadBox.onCancel() + Game.cancelLogin() + CharacterList.show() + end + + -- save last used character + Configs.set('lastUsedCharacter', charInfo.characterName) +end + -- public functions function CharacterList.create(characters, premDays) if charactersWindow then @@ -65,21 +86,10 @@ end function CharacterList.doLogin() local selected = charactersWindow:getChildById('characterList'):getFocusedChild() if selected then - --if Game.isOnline() then - -- Game.logout() - --end - - Game.loginWorld(EnterGame.account, EnterGame.password, selected.worldHost, selected.worldPort, selected.characterName) - CharacterList.hide() - - loadBox = displayCancelBox('Please wait', 'Connecting to game server...') - function loadBox.onCancel() - Game.logout() - CharacterList.show() - end - - -- save last used character - Configs.set('lastUsedCharacter', selected.characterName) + local charInfo = { worldHost = selected.worldHost, + worldPort = selected.worldPort, + characterName = selected.characterName } + tryLogin(charInfo) else displayErrorBox('Error', 'You must select a character to login!') end diff --git a/src/framework/net/connection.cpp b/src/framework/net/connection.cpp index cbb5d2ac..f3300cb0 100644 --- a/src/framework/net/connection.cpp +++ b/src/framework/net/connection.cpp @@ -34,6 +34,7 @@ Connection::Connection() : m_socket(g_ioService) { m_connected = false; + m_connecting = false; } void Connection::poll() @@ -50,6 +51,7 @@ void Connection::terminate() void Connection::connect(const std::string& host, uint16 port, const SimpleCallback& connectCallback) { m_connected = false; + m_connecting = true; m_connectCallback = connectCallback; asio::ip::tcp::resolver::query query(host, Fw::unsafeCast(port)); @@ -61,22 +63,23 @@ void Connection::connect(const std::string& host, uint16 port, const SimpleCallb void Connection::close() { - if(!m_connected) + if(!m_connected && !m_connecting) return; + m_connecting = false; m_connected = false; + m_connectCallback = nullptr; + m_errorCallback = nullptr; + m_recvCallback = nullptr; m_readTimer.cancel(); m_writeTimer.cancel(); if(m_socket.is_open()) { - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + boost::system::error_code ec; + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); m_socket.close(); } - - m_connectCallback = nullptr; - m_errorCallback = nullptr; - m_recvCallback = nullptr; } void Connection::write(uint8* buffer, uint16 size) @@ -129,9 +132,10 @@ void Connection::onConnect(const boost::system::error_code& error) { m_readTimer.cancel(); - m_connected = true; + m_connecting = false; if(!error) { + m_connected = true; if(m_connectCallback) g_dispatcher.addEvent(m_connectCallback); } else @@ -174,7 +178,7 @@ void Connection::handleError(const boost::system::error_code& error) if(error != asio::error::operation_aborted) { if(m_errorCallback) g_dispatcher.addEvent(std::bind(m_errorCallback, error)); - if(m_connected) + if(m_connected || m_connecting) close(); } } diff --git a/src/framework/net/connection.h b/src/framework/net/connection.h index a0be19bb..bd962d3d 100644 --- a/src/framework/net/connection.h +++ b/src/framework/net/connection.h @@ -51,6 +51,7 @@ public: void setErrorCallback(const ErrorCallback& errorCallback) { m_errorCallback = errorCallback; } + bool isConnecting() const { return m_connecting; } bool isConnected() const { return m_connected; } private: @@ -73,6 +74,7 @@ private: uint8 m_recvBuffer[65538]; uint16 m_recvSize; bool m_connected; + bool m_connecting; }; #endif diff --git a/src/framework/net/protocol.cpp b/src/framework/net/protocol.cpp index 76c25b3c..014b6b20 100644 --- a/src/framework/net/protocol.cpp +++ b/src/framework/net/protocol.cpp @@ -44,6 +44,20 @@ void Protocol::disconnect() } } +bool Protocol::isConnected() +{ + if(m_connection && m_connection->isConnected()) + return true; + return false; +} + +bool Protocol::isConnecting() +{ + if(m_connection && m_connection->isConnecting()) + return true; + return false; +} + void Protocol::send(OutputMessage& outputMessage) { // encrypt diff --git a/src/framework/net/protocol.h b/src/framework/net/protocol.h index 6a0b0aaf..72a1f691 100644 --- a/src/framework/net/protocol.h +++ b/src/framework/net/protocol.h @@ -37,6 +37,9 @@ public: void connect(const std::string& host, uint16 port); void disconnect(); + bool isConnected(); + bool isConnecting(); + void send(OutputMessage& outputMessage); void recv(); diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 3325d00e..70e3f026 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -374,8 +374,7 @@ void UIWidget::insertChild(int index, const UIWidgetPtr& child) void UIWidget::removeChild(const UIWidgetPtr& child) { // remove from children list - auto it = std::find(m_children.begin(), m_children.end(), child); - if(it != m_children.end()) { + if(hasChild(child)) { // defocus if needed bool focusAnother = false; if(m_focusedChild == child) { @@ -383,9 +382,10 @@ void UIWidget::removeChild(const UIWidgetPtr& child) focusAnother = true; } - // unlock child if it was locked - unlockChild(child); + if(isChildLocked(child)) + unlockChild(child); + auto it = std::find(m_children.begin(), m_children.end(), child); m_children.erase(it); // reset child parent @@ -397,7 +397,7 @@ void UIWidget::removeChild(const UIWidgetPtr& child) // update child states child->updateStates(); - if(focusAnother) + if(focusAnother && !m_focusedChild) focusPreviousChild(Fw::ActiveFocusReason); } else logError("attempt to remove an unknown child from a UIWidget"); @@ -474,7 +474,8 @@ void UIWidget::lockChild(const UIWidgetPtr& child) assert(hasChild(child)); // prevent double locks - unlockChild(child); + if(isChildLocked(child)) + unlockChild(child); // disable all other children for(const UIWidgetPtr& otherChild : m_children) { @@ -488,7 +489,7 @@ void UIWidget::lockChild(const UIWidgetPtr& child) // lock child focus if(child->isFocusable()) - focusChild(child, Fw::ActiveFocusReason); + focusChild(child, Fw::ActiveFocusReason); moveChildToTop(child); } @@ -506,10 +507,12 @@ void UIWidget::unlockChild(const UIWidgetPtr& child) m_lockedChildren.erase(it); - // find new chick to lock + // find new child to lock UIWidgetPtr lockedChild; - if(m_lockedChildren.size() > 0) + if(m_lockedChildren.size() > 0) { lockedChild = m_lockedChildren.front(); + assert(hasChild(lockedChild)); + } for(const UIWidgetPtr& otherChild : m_children) { // lock new child @@ -523,6 +526,19 @@ void UIWidget::unlockChild(const UIWidgetPtr& child) else otherChild->setEnabled(true); } + + if(lockedChild) { + if(lockedChild->isFocusable()) + focusChild(lockedChild, Fw::ActiveFocusReason); + + moveChildToTop(lockedChild); + } +} + +bool UIWidget::isChildLocked(const UIWidgetPtr& child) +{ + auto it = std::find(m_lockedChildren.begin(), m_lockedChildren.end(), child); + return it != m_lockedChildren.end(); } void UIWidget::updateParentLayout() diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index b5256bfd..7b58d0cf 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -135,6 +135,7 @@ public: void moveChildToTop(const UIWidgetPtr& child); void lockChild(const UIWidgetPtr& child); void unlockChild(const UIWidgetPtr& child); + bool isChildLocked(const UIWidgetPtr& child); void updateParentLayout(); void updateLayout(); diff --git a/src/otclient/core/game.cpp b/src/otclient/core/game.cpp index ea181225..1fd1f4b8 100644 --- a/src/otclient/core/game.cpp +++ b/src/otclient/core/game.cpp @@ -43,26 +43,52 @@ void Game::loginWorld(const std::string& account, const std::string& password, c m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName); } +void Game::cancelLogin() +{ + if(m_protocolGame->isConnected()) { + logout(); + } else if(m_protocolGame->isConnecting()) { + m_protocolGame->disconnect(); + m_protocolGame.reset(); + } +} + void Game::logout() { m_protocolGame->sendLogout(); - processLogout(); +} + +void Game::processConnectionError(const boost::system::error_code& error) +{ + // connection errors only have meaning if we still have a protocol + if(m_protocolGame) { + g_lua.callGlobalField("Game", "onConnectionError", error.message()); + + if(m_online) + processLogout(); + + // disconnect isn't needed, we are already disconnected + m_protocolGame.reset(); + } } void Game::processLogin(const LocalPlayerPtr& localPlayer) { m_localPlayer = localPlayer; m_online = true; + g_lua.callGlobalField("Game", "onLogin", m_localPlayer); } void Game::processLogout() { g_lua.callGlobalField("Game", "onLogout", m_localPlayer); + if(m_protocolGame) { m_protocolGame->disconnect(); m_protocolGame.reset(); } + m_localPlayer.reset(); m_online = false; } diff --git a/src/otclient/core/game.h b/src/otclient/core/game.h index 810e7030..42d7f6fa 100644 --- a/src/otclient/core/game.h +++ b/src/otclient/core/game.h @@ -39,6 +39,7 @@ public: void cancelLogin(); void logout(); + void processConnectionError(const boost::system::error_code& error); void processLogin(const LocalPlayerPtr& localPlayer); void processLogout(); diff --git a/src/otclient/net/protocolgame.cpp b/src/otclient/net/protocolgame.cpp index b3339c57..9f847959 100644 --- a/src/otclient/net/protocolgame.cpp +++ b/src/otclient/net/protocolgame.cpp @@ -70,7 +70,6 @@ void ProtocolGame::onRecv(InputMessage& inputMessage) void ProtocolGame::onError(const boost::system::error_code& error) { - // already disconnected, just fire onLogout - g_game.processLogout(); + g_game.processConnectionError(error); } diff --git a/src/otclient/otclientluafunctions.cpp b/src/otclient/otclientluafunctions.cpp index 0bdfce89..211479c6 100644 --- a/src/otclient/otclientluafunctions.cpp +++ b/src/otclient/otclientluafunctions.cpp @@ -61,6 +61,8 @@ void OTClient::registerLuaFunctions() g_lua.registerClass(); g_lua.bindClassStaticFunction("loginWorld", std::bind(&Game::loginWorld, &g_game, _1, _2, _3, _4, _5)); g_lua.bindClassStaticFunction("logout", std::bind(&Game::logout, &g_game)); + g_lua.bindClassStaticFunction("cancelLogin", std::bind(&Game::cancelLogin, &g_game)); + g_lua.bindClassStaticFunction("isOnline", std::bind(&Game::isOnline, &g_game)); g_lua.registerClass(); g_lua.bindClassStaticFunction("create", &UIWidget::create);