From e9e4dcd71b6cae55b3af16d5bb6b5dadce87e559 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Wed, 13 Mar 2013 20:55:20 -0300 Subject: [PATCH] Improve walk when lagging --- modules/game_interface/gameinterface.lua | 47 +++++++++++++++--------- src/client/game.cpp | 39 ++++++++++++++++---- src/client/game.h | 5 ++- src/client/localplayer.cpp | 7 +++- src/client/localplayer.h | 1 + src/client/luafunctions.cpp | 1 + src/framework/net/connection.cpp | 4 +- src/framework/net/connection.h | 2 + src/framework/net/protocol.h | 2 + 9 files changed, 79 insertions(+), 29 deletions(-) diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index 2504539f..2272abe5 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -104,8 +104,8 @@ function bindKeys() g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel) g_keyboard.bindKeyPress('Ctrl+=', function() gameMapPanel:zoomIn() end, gameRootPanel) g_keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel) - g_keyboard.bindKeyDown('Ctrl+Q', logout, gameRootPanel) - g_keyboard.bindKeyDown('Ctrl+L', logout, gameRootPanel) + g_keyboard.bindKeyDown('Ctrl+Q', tryLogout, gameRootPanel) + g_keyboard.bindKeyDown('Ctrl+L', tryLogout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+.', nextViewMode, gameRootPanel) end @@ -215,8 +215,8 @@ function tryExit() return true end - local exitFunc = function() logout() forceExit() end - local logoutFunc = function() logout() exitWindow:destroy() exitWindow = nil end + local exitFunc = function() g_game.safeLogout() forceExit() end + local logoutFunc = function() g_game.safeLogout() exitWindow:destroy() exitWindow = nil end local cancelFunc = function() exitWindow:destroy() exitWindow = nil end exitWindow = displayGeneralBox(tr('Exit'), tr("If you shut down the program, your character might stay in the game.\nClick on 'Logout' to ensure that you character leaves the game properly.\nClick on 'Exit' if you want to exit the program without logging out your character."), @@ -228,13 +228,6 @@ function tryExit() return true end -function logout() - if g_game.isOnline() then - g_game.safeLogout() - return true - end -end - function tryLogout() if not g_game.isOnline() then exit() @@ -245,13 +238,33 @@ function tryLogout() return end - local yesCallback = function() logout() logoutWindow:destroy() logoutWindow=nil end - local noCallback = function() logoutWindow:destroy() logoutWindow=nil end + if not g_game.isConnectionOk() then + local yesCallback = function() + g_game.forceLogout() + logoutWindow=nil + end + local noCallback = function() + logoutWindow=nil + end + + logoutWindow = displayGeneralBox(tr('Logout'), tr('Your connection is failing, if you logout now your character will be still online, do you want to force logout?'), { + { text=tr('Yes'), callback=yesCallback }, + { text=tr('No'), callback=noCallback }, + anchor=AnchorHorizontalCenter}, yesCallback, noCallback) + else + local yesCallback = function() + g_game.safeLogout() + logoutWindow=nil + end + local noCallback = function() + logoutWindow=nil + end - logoutWindow = displayGeneralBox(tr('Logout'), tr('Are you sure you want to logout?'), { - { text=tr('Yes'), callback=yesCallback }, - { text=tr('No'), callback=noCallback }, - anchor=AnchorHorizontalCenter}, yesCallback, noCallback) + logoutWindow = displayGeneralBox(tr('Logout'), tr('Are you sure you want to logout?'), { + { text=tr('Yes'), callback=yesCallback }, + { text=tr('No'), callback=noCallback }, + anchor=AnchorHorizontalCenter}, yesCallback, noCallback) + end end function stopSmartWalk() diff --git a/src/client/game.cpp b/src/client/game.cpp index 8fc0a9c9..058c1811 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -99,6 +99,11 @@ void Game::resetGameStates() m_walkEvent = nullptr; } + if(m_checkConnectionEvent) { + m_checkConnectionEvent->cancel(); + m_checkConnectionEvent = nullptr; + } + m_containers.clear(); m_vips.clear(); m_gmActions.clear(); @@ -183,6 +188,16 @@ void Game::processGameStart() g_game.ping(); }, m_pingDelay); } + + m_checkConnectionEvent = g_dispatcher.cycleEvent([this] { + if(!g_game.isConnectionOk() && !m_connectionFailWarned) { + g_lua.callGlobalField("g_game", "onConnectionFailing", true); + m_connectionFailWarned = true; + } else if(g_game.isConnectionOk() && m_connectionFailWarned) { + g_lua.callGlobalField("g_game", "onConnectionFailing", false); + m_connectionFailWarned = false; + } + }, 1000); } void Game::processGameEnd() @@ -190,6 +205,11 @@ void Game::processGameEnd() m_online = false; g_lua.callGlobalField("g_game", "onGameEnd"); + if(m_connectionFailWarned) { + g_lua.callGlobalField("g_game", "onConnectionFailing", false); + m_connectionFailWarned = false; + } + // reset game state resetGameStates(); @@ -551,6 +571,7 @@ bool Game::walk(Otc::Direction direction) // check we can walk and add new walk event if false if(!m_localPlayer->canWalk(direction)) { + /* if(m_lastWalkDir != direction) { // must add a new walk event float ticks = m_localPlayer->getStepTicksLeft(); @@ -562,6 +583,7 @@ bool Game::walk(Otc::Direction direction) } m_walkEvent = g_dispatcher.scheduleEvent([=] { walk(direction); }, ticks); } + */ return false; } @@ -697,15 +719,16 @@ void Game::autoWalk(std::vector dirs) auto it = dirs.begin(); Otc::Direction direction = *it; - if(m_localPlayer->canWalk(direction)) { - TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction)); - if(toTile && toTile->isWalkable() && !m_localPlayer->isServerWalking()) { - m_localPlayer->preWalk(direction); + if(!m_localPlayer->canWalk(direction)) + return; - if(getFeature(Otc::GameForceFirstAutoWalkStep)) { - forceWalk(direction); - dirs.erase(it); - } + TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction)); + if(toTile && toTile->isWalkable() && !m_localPlayer->isServerWalking()) { + m_localPlayer->preWalk(direction); + + if(getFeature(Otc::GameForceFirstAutoWalkStep)) { + forceWalk(direction); + dirs.erase(it); } } diff --git a/src/client/game.h b/src/client/game.h index ac878009..c2834af9 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -231,7 +231,7 @@ public: void openRuleViolation(const std::string& reporter); void closeRuleViolation(const std::string& reporter); void cancelRuleViolation(); - + // reports void reportBug(const std::string& comment); void reportRuleViolation(const std::string& target, int reason, int action, const std::string& comment, const std::string& statement, int statementId, bool ipBanishment); @@ -281,6 +281,7 @@ public: bool isDead() { return m_dead; } bool isAttacking() { return !!m_attackingCreature; } bool isFollowing() { return !!m_followingCreature; } + bool isConnectionOk() { return m_protocolGame && m_protocolGame->getElapsedTicksSinceLastRead() < 5000; } int getPing() { return m_ping >= 0 ? std::max(m_ping, m_pingTimer.elapsed_millis()) : -1; } ContainerPtr getContainer(int index) { return m_containers[index]; } @@ -340,6 +341,8 @@ private: std::bitset m_features; ScheduledEventPtr m_pingEvent; ScheduledEventPtr m_walkEvent; + ScheduledEventPtr m_checkConnectionEvent; + bool m_connectionFailWarned; int m_protocolVersion; int m_clientVersion; std::string m_clientSignature; diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 6ddc9403..23f3792c 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -83,7 +83,7 @@ bool LocalPlayer::canWalk(Otc::Direction direction) return false; // cannot walk while already walking - if(m_walking && !prewalkTimeouted) + if(m_walking && (!prewalkTimeouted || m_secondPreWalk)) return false; return true; @@ -95,6 +95,7 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos) if(m_preWalking) { // switch to normal walking m_preWalking = false; + m_secondPreWalk = false; m_lastPrewalkDone = true; // if is to the last prewalk destination, updates the walk preserving the animation if(newPos == m_lastPrewalkDestination) { @@ -118,7 +119,8 @@ void LocalPlayer::preWalk(Otc::Direction direction) Position newPos = m_position.translatedToDirection(direction); // avoid reanimating prewalks - if(m_preWalking && m_lastPrewalkDestination == newPos) { + if(m_preWalking) { + m_secondPreWalk = true; return; } @@ -277,6 +279,7 @@ void LocalPlayer::terminateWalk() { Creature::terminateWalk(); m_preWalking = false; + m_secondPreWalk = false; m_idleTimer.restart(); auto self = asLocalPlayer(); diff --git a/src/client/localplayer.h b/src/client/localplayer.h index d2ba0774..bf898189 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -126,6 +126,7 @@ private: ticks_t m_walkLockExpiration; stdext::boolean m_preWalking; stdext::boolean m_lastPrewalkDone; + stdext::boolean m_secondPreWalk; stdext::boolean m_serverWalking; stdext::boolean m_knownCompletePath; diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 559811cf..a18dc6ca 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -230,6 +230,7 @@ void Client::registerLuaFunctions() g_lua.bindSingletonFunction("g_game", "isDead", &Game::isDead, &g_game); g_lua.bindSingletonFunction("g_game", "isAttacking", &Game::isAttacking, &g_game); g_lua.bindSingletonFunction("g_game", "isFollowing", &Game::isFollowing, &g_game); + g_lua.bindSingletonFunction("g_game", "isConnectionOk", &Game::isConnectionOk, &g_game); g_lua.bindSingletonFunction("g_game", "getPing", &Game::getPing, &g_game); g_lua.bindSingletonFunction("g_game", "getContainer", &Game::getContainer, &g_game); g_lua.bindSingletonFunction("g_game", "getContainers", &Game::getContainers, &g_game); diff --git a/src/framework/net/connection.cpp b/src/framework/net/connection.cpp index 07ee9ce1..1c53d235 100644 --- a/src/framework/net/connection.cpp +++ b/src/framework/net/connection.cpp @@ -125,7 +125,7 @@ void Connection::write(uint8* buffer, size_t size) m_outputStream = std::shared_ptr(new asio::streambuf); m_delayedWriteTimer.cancel(); - m_delayedWriteTimer.expires_from_now(boost::posix_time::milliseconds(1)); + m_delayedWriteTimer.expires_from_now(boost::posix_time::milliseconds(10)); m_delayedWriteTimer.async_wait(std::bind(&Connection::onCanWrite, asConnection(), std::placeholders::_1)); } @@ -215,6 +215,7 @@ void Connection::onResolve(const boost::system::error_code& error, asio::ip::bas void Connection::onConnect(const boost::system::error_code& error) { m_readTimer.cancel(); + m_activityTimer.restart(); if(error == asio::error::operation_aborted) return; @@ -263,6 +264,7 @@ void Connection::onWrite(const boost::system::error_code& error, size_t writeSiz void Connection::onRecv(const boost::system::error_code& error, size_t recvSize) { m_readTimer.cancel(); + m_activityTimer.restart(); if(error == asio::error::operation_aborted) return; diff --git a/src/framework/net/connection.h b/src/framework/net/connection.h index dbeacae1..e80e0b1c 100644 --- a/src/framework/net/connection.h +++ b/src/framework/net/connection.h @@ -61,6 +61,7 @@ public: boost::system::error_code getError() { return m_error; } bool isConnecting() { return m_connecting; } bool isConnected() { return m_connected; } + ticks_t getElapsedTicksSinceLastRead() { return m_connected ? m_activityTimer.elapsed_millis() : -1; } ConnectionPtr asConnection() { return static_self_cast(); } @@ -91,6 +92,7 @@ protected: bool m_connected; bool m_connecting; boost::system::error_code m_error; + stdext::timer m_activityTimer; friend class Server; }; diff --git a/src/framework/net/protocol.h b/src/framework/net/protocol.h index 7c71f588..4070c13c 100644 --- a/src/framework/net/protocol.h +++ b/src/framework/net/protocol.h @@ -42,6 +42,8 @@ public: bool isConnected(); bool isConnecting(); + ticks_t getElapsedTicksSinceLastRead() { return m_connection ? m_connection->getElapsedTicksSinceLastRead() : -1; } + ConnectionPtr getConnection() { return m_connection; } void setConnection(const ConnectionPtr& connection) { m_connection = connection; }