// // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2013 Glenn De Jonghe // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "online/online_player_profile.hpp" #include "achievements/achievements_manager.hpp" #include "config/player_manager.hpp" #include "config/user_config.hpp" #include "guiengine/message_queue.hpp" #include "guiengine/screen.hpp" #include "online/online_profile.hpp" #include "online/profile_manager.hpp" #include "online/servers_manager.hpp" #include "states_screens/online_profile_friends.hpp" #include "states_screens/user_screen.hpp" #include "states_screens/dialogs/change_password_dialog.hpp" #include "states_screens/dialogs/notification_dialog.hpp" #include "states_screens/dialogs/user_info_dialog.hpp" #include "utils/log.hpp" #include "utils/translation.hpp" #include #include #include #include using namespace irr; using namespace Online; namespace Online { // ------------------------------------------------------------------------ /** Adds the login credential to a http request. It sets the name of * the script to invokce, token, and user id. * \param request The http request. * \param action the action performed */ void OnlinePlayerProfile::setUserDetails(HTTPRequest *request, const std::string &action, const std::string &url_path) const { if (url_path.size()) { request->setApiURL(url_path, action); } else // default path { request->setApiURL(API::USER_PATH, action); } if (m_profile) request->addParameter("userid", m_profile->getID()); if (m_online_state == OS_SIGNED_IN) request->addParameter("token", m_token); } // setUserDetails // ======================================================================== OnlinePlayerProfile::OnlinePlayerProfile(const XMLNode *player) : PlayerProfile(player) { m_online_state = OS_SIGNED_OUT; m_token = ""; m_profile = NULL; } // OnlinePlayerProfile // ------------------------------------------------------------------------ OnlinePlayerProfile::OnlinePlayerProfile(const core::stringw &name, bool is_guest) : PlayerProfile(name, is_guest) { m_online_state = OS_SIGNED_OUT; m_token = ""; m_profile = NULL; } // OnlinePlayerProfile // ------------------------------------------------------------------------ /** Request a login using the saved credentials of the user. */ void OnlinePlayerProfile::requestSavedSession() { SignInRequest *request = NULL; if (m_online_state == OS_SIGNED_OUT && hasSavedSession()) { request = new SignInRequest(true); setUserDetails(request, "saved-session"); // The userid must be taken from the saved data, // setUserDetails takes it from current data. request->addParameter("userid", getSavedUserId()); request->addParameter("token", getSavedToken() ); request->queue(); m_online_state = OS_SIGNING_IN; } } // requestSavedSession // ------------------------------------------------------------------------ /** Create a signin request. * \param username Name of user. * \param password Password. */ OnlinePlayerProfile::SignInRequest* OnlinePlayerProfile::requestSignIn(const core::stringw &username, const core::stringw &password) { // If the player changes the online account, there can be a // logout stil happening. assert(m_online_state == OS_SIGNED_OUT || m_online_state == OS_SIGNING_OUT); SignInRequest * request = new SignInRequest(false); // We can't use setUserDetail here, since there is no token yet request->setApiURL(API::USER_PATH, "connect"); request->addParameter("username", username); request->addParameter("password", password); request->addParameter("save-session", rememberPassword() ? "true" : "false"); request->queue(); m_online_state = OS_SIGNING_IN; return request; } // requestSignIn // ------------------------------------------------------------------------ /** Called when the signin request is finished. */ void OnlinePlayerProfile::SignInRequest::callback() { PlayerManager::getCurrentPlayer()->signIn(isSuccess(), getXMLData()); GUIEngine::Screen *screen = GUIEngine::getCurrentScreen(); BaseUserScreen *login = dynamic_cast(screen); // If the login is successful, reset any saved session of other // local players using the same online account (which are now invalid) if (isSuccess()) { PlayerProfile *current = PlayerManager::getCurrentPlayer(); for (unsigned int i = 0; i < PlayerManager::get()->getNumPlayers(); i++) { PlayerProfile *player = PlayerManager::get()->getPlayer(i); if(player != current && player->hasSavedSession() && player->getLastOnlineName() == current->getLastOnlineName()) { player->clearSession(); } } } if (login) { if(isSuccess()) login->loginSuccessful(); else login->loginError(getInfo()); } // if dialog } // SignInRequest::callback // ------------------------------------------------------------------------ /** Checks the server respond after a login attempt. If the login * was successful, it marks the user as logged in, and (if requested) * saves data to be able to login next time. * \param success If the answer from the server indicated a * successful login attemp. * \param input Xml tree with the complete server response. */ void OnlinePlayerProfile::signIn(bool success, const XMLNode * input) { if (success) { core::stringw username(""); uint32_t userid(0); int token_fetched = input->get("token", &m_token); int username_fetched = input->get("username", &username); int userid_fetched = input->get("userid", &userid); setLastOnlineName(username); m_profile = new OnlineProfile(userid, username, true); assert(token_fetched && username_fetched && userid_fetched); m_online_state = OS_SIGNED_IN; if(rememberPassword()) { saveSession(getOnlineId(), getToken()); } ProfileManager::get()->addPersistent(m_profile); std::string achieved_string(""); // Even if no achievements were sent, we have to call sync // in order to upload local achievements to the server input->get("achieved", &achieved_string); std::vector achieved_ids = StringUtils::splitToUInt(achieved_string, ' '); PlayerManager::getCurrentAchievementsStatus()->sync(achieved_ids); m_profile->fetchFriends(); } // if success else { m_online_state = OS_SIGNED_OUT; } } // signIn // ------------------------------------------------------------------------ /** Requests a sign out from the server. If the user should be remembered, * a 'client-quit' request is sent (which will log the user out, but * remember the token), otherwise a 'disconnect' is sent. */ void OnlinePlayerProfile::requestSignOut() { assert(m_online_state == OS_SIGNED_IN || m_online_state == OS_GUEST); // ---------------------------------------------------------------- class SignOutRequest : public XMLRequest { private: PlayerProfile *m_player; virtual void callback() { m_player->signOut(isSuccess(), getXMLData(), getInfo()); } public: /** Sign out request, which have the highest priority (same as * quit-stk request). This allows the final logout request at * the end of STK to be handled, even if a quit request gets * added (otherwise if quit has higher priority, the quit can * be executed before signout, resulting in players not being * logged out properly). It also guarantees that the logout * happens before a following logout. */ SignOutRequest(PlayerProfile *player) : XMLRequest(true,/*priority*/RequestManager::HTTP_MAX_PRIORITY) { m_player = player; m_player->setUserDetails(this, m_player->rememberPassword() ? "client-quit" : "disconnect"); setAbortable(false); } // SignOutRequest }; // SignOutRequest // ---------------------------------------------------------------- HTTPRequest *request = new SignOutRequest(this); request->queue(); m_online_state = OS_SIGNING_OUT; } // requestSignOut // ------------------------------------------------------------------------ /** Callback once the logout event has been processed. * \param success If the request was successful. * \param input */ void OnlinePlayerProfile::signOut(bool success, const XMLNode *input, const irr::core::stringw &info) { GUIEngine::Screen *screen = GUIEngine::getCurrentScreen(); BaseUserScreen *user_screen = dynamic_cast(screen); // We can't do much of error handling here, no screen waits for // a logout to finish, so we can only log the message to screen, // and otherwise mark the player logged out internally. if (!success) { Log::warn("OnlinePlayerProfile::signOut", "There were some connection issues while signing out. " "Report a bug if this caused issues."); Log::warn("OnlinePlayerProfile::signOut", core::stringc(info.c_str()).c_str()); if (user_screen) user_screen->logoutError(info); } else { if (user_screen) user_screen->logoutSuccessful(); } ProfileManager::get()->clearPersistent(); m_profile = NULL; m_online_state = OS_SIGNED_OUT; // Discard token if session should not be saved. if (!rememberPassword()) clearSession(); } // signOut // ------------------------------------------------------------------------ /** Sends a request to the server to see if any new information is * available. (online friends, notifications, etc.). */ void OnlinePlayerProfile::requestPoll() const { assert(m_online_state == OS_SIGNED_IN); OnlinePlayerProfile::PollRequest *request = new OnlinePlayerProfile::PollRequest(); setUserDetails(request, "poll"); request->queue(); } // requestPoll() // ------------------------------------------------------------------------ /** Callback for the poll request. Parses the information and spawns * notifications accordingly. */ void OnlinePlayerProfile::PollRequest::callback() { // connection error if (!isSuccess()) { Log::error("Online Player Profile", "Poll request failed"); return; } if (!PlayerManager::getCurrentPlayer()->isLoggedIn()) return; if (PlayerManager::getCurrentPlayer()->getProfile()->hasFetchedFriends()) { std::string online_friends_string(""); if (getXMLData()->get("online", &online_friends_string) == 1) { std::vector online_friends = StringUtils::splitToUInt(online_friends_string, ' '); // flag that indicates if a current online friend went offline bool went_offline = false; // iterate over all friends and find out if they come online or not // filling the notification messages std::vector friends = PlayerManager::getCurrentPlayer()->getProfile()->getFriends(); std::vector to_notify; for (unsigned int i = 0; i < friends.size(); ++i) { bool now_online = false; std::vector::iterator found_friend = std::find(online_friends.begin(), online_friends.end(), friends[i]); if (found_friend != online_friends.end()) { now_online = true; online_friends.erase(found_friend); } OnlineProfile * profile = ProfileManager::get()->getProfileByID(friends[i]); OnlineProfile::RelationInfo * relation_info = profile->getRelationInfo(); if (relation_info->isOnline()) { if (!now_online) // the friend went offline { relation_info->setOnline(false); went_offline = true; } } else { if (now_online) // friend came online { relation_info->setOnline(true); // Do this because a user might have accepted // a pending friend request. profile->setFriend(); to_notify.push_back(profile->getUserName()); } } } if (to_notify.size() > 0) { core::stringw message(""); if(to_notify.size() == 1) { message = _("%s is now online.", to_notify[0]); } else if(to_notify.size() == 2) { message = _("%s and %s are now online.", to_notify[0], to_notify[1] ); } else if(to_notify.size() == 3) { message = _("%s, %s and %s are now online.", to_notify[0], to_notify[1], to_notify[2]); } else if(to_notify.size() > 3) { message = _("%d friends are now online.", to_notify.size()); } MessageQueue::add(MessageQueue::MT_FRIEND, message); } else if (went_offline) { OnlineProfileFriends::getInstance()->refreshFriendsList(); } } } else { PlayerManager::getCurrentPlayer()->getProfile()->fetchFriends(); } int friend_request_count = 0; for (unsigned int i = 0; i < getXMLData()->getNumNodes(); i++) { const XMLNode * node = getXMLData()->getNode(i); if (node->getName() == "new_friend_request") { OnlineProfile::RelationInfo * ri = new OnlineProfile::RelationInfo("New", false, true, true); OnlineProfile * p = new OnlineProfile(node); p->setRelationInfo(ri); ProfileManager::get()->addPersistent(p); friend_request_count++; } } if (friend_request_count > 0) { core::stringw message(""); if (friend_request_count > 1) { message = _("You have %d new friend requests!", friend_request_count); } else { message = _("You have a new friend request!"); } MessageQueue::add(MessageQueue::MT_FRIEND, message); OnlineProfileFriends::getInstance()->refreshFriendsList(); } } // PollRequest::callback // ------------------------------------------------------------------------ /** \return the online id, or 0 if the user is not signed in. */ uint32_t OnlinePlayerProfile::getOnlineId() const { if (m_online_state == OS_SIGNED_IN) { assert(m_profile != NULL); return m_profile->getID(); } return 0; } // getOnlineId } // namespace Online