diff options
Diffstat (limited to 'src/gui/whoisonline.cpp')
-rw-r--r-- | src/gui/whoisonline.cpp | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/src/gui/whoisonline.cpp b/src/gui/whoisonline.cpp new file mode 100644 index 000000000..84485b995 --- /dev/null +++ b/src/gui/whoisonline.cpp @@ -0,0 +1,550 @@ +/* + * The Mana World + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 Andrei Karas + * + * This file is part of The Mana World. + * + * 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 2 of the License, or + * 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 "whoisonline.h" + +#include <SDL.h> +#include <SDL_thread.h> +#include <vector> +#include <algorithm> + +#include "gui/viewport.h" +#include "gui/widgets/button.h" +#include "gui/widgets/browserbox.h" +#include "gui/widgets/scrollarea.h" +#include "gui/widgets/chattab.h" + +#include "actorspritemanager.h" +#include "client.h" +#include "configuration.h" +#include "localplayer.h" +#include "playerrelations.h" +#include "main.h" + +#include "gui/chat.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +// Curl should be included after Guichan to avoid Windows redefinitions +#include <curl/curl.h> + +bool stringCompare(const std::string &left, const std::string &right); + +bool stringCompare(const std::string &left, const std::string &right ) +{ + for (std::string::const_iterator lit = left.begin(), + rit = right.begin(); + lit != left.end() && rit != right.end(); ++lit, ++rit) + { + if (tolower(*lit) < tolower(*rit)) + return true; + else if (tolower(*lit) > tolower(*rit)) + return false; + } + if (left.size() < right.size()) + return true; + return false; +} + +WhoIsOnline::WhoIsOnline(): + Window(_("Who Is Online - Updating")), + mThread(NULL), + mDownloadStatus(UPDATE_LIST), + mDownloadComplete(true), + mDownloadedBytes(0), + mMemoryBuffer(NULL), + mCurlError(new char[CURL_ERROR_SIZE]), + mAllowUpdate(true), + mShowLevel(false) +{ + mCurlError[0] = 0; + setWindowName("WhoIsOnline"); + + const int h = 350; + const int w = 200; + setDefaultSize(w, h, ImageRect::CENTER); +// setContentSize(w, h); + setCloseButton(true); + setResizable(true); + + mUpdateButton = new Button(_("Update"), "update", this); + mUpdateButton->setEnabled(false); + mUpdateButton->setDimension(gcn::Rectangle(5, 5, w - 10, 20 + 5)); + + mBrowserBox = new BrowserBox(); + mScrollArea = new ScrollArea(mBrowserBox); + mScrollArea->setOpaque(false); + mBrowserBox->setOpaque(false); + mBrowserBox->setHighlightMode(BrowserBox::BACKGROUND); + mScrollArea->setDimension(gcn::Rectangle(5, 20 + 10, w - 10, h - 10 - 30)); + mScrollArea->setSize(w - 10, h - 10 - 30); + mBrowserBox->setLinkHandler(this); + + add(mUpdateButton); + add(mScrollArea); + + mUpdateTimer = 0; + setLocationRelativeTo(getParent()); + + loadWindowState(); + + download(); + + config.addListener("updateOnlineList", this); + mUpdateOnlineList = config.getBoolValue("updateOnlineList"); +} + +WhoIsOnline::~WhoIsOnline() +{ + config.removeListener("updateOnlineList", this); + + if (mThread && SDL_GetThreadID(mThread)) + SDL_WaitThread(mThread, NULL); + + free(mMemoryBuffer); + mMemoryBuffer = 0; + + // Remove possibly leftover temporary download + delete[] mCurlError; +} + +void WhoIsOnline::handleLink(const std::string& link, gcn::MouseEvent *event) +{ + if (!event || event->getButton() == gcn::MouseEvent::LEFT) + { + if (chatWindow) + { + if (config.getBoolValue("whispertab")) + chatWindow->localChatInput("/q " + link); + else + chatWindow->addInputText("/w \"" + link + "\" "); + } + } + else if (event->getButton() == gcn::MouseEvent::RIGHT) + { + if (player_node && link == player_node->getName()) + return; + + if (viewport) + { + if (actorSpriteManager) + { + Being* being = actorSpriteManager->findBeingByName( + link, Being::PLAYER); + + if (being && viewport) + { + viewport->showPopup(event->getX(), event->getY(), being); + return; + } + } + viewport->showPlayerPopup(link); + } + } +} + +void WhoIsOnline::loadList() +{ + if (!mMemoryBuffer) + return; + + // Reallocate and include terminating 0 character + mMemoryBuffer = static_cast<char*>( + realloc(mMemoryBuffer, mDownloadedBytes + 1)); + mMemoryBuffer[mDownloadedBytes] = '\0'; + + mBrowserBox->clearRows(); + bool listStarted(false); + std::string lineStr; + int numOnline(0); + std::vector<std::string> friends; + std::vector<std::string> neutral; + std::vector<std::string> disregard; + + // Tokenize and add each line separately + char *line = strtok(mMemoryBuffer, "\n"); + const std::string gmText = "(GM)"; + mOnlinePlayers.clear(); + + mShowLevel = config.getBoolValue("showlevel"); + + while (line != NULL) + { + std::string nick; + lineStr = line; + trim(lineStr); + if (listStarted == true) + { + size_t found; + found = lineStr.find(" users are online."); + if (found == std::string::npos) + { + int level = 0; + + std::string::size_type pos = 0; + if (lineStr.length() > 24) + { + nick = lineStr.substr(0, 24); + lineStr = lineStr.substr(25); + } + else + { + nick = lineStr; + lineStr = ""; + } + trim(nick); + + pos = lineStr.find(gmText, 0); + if (pos != std::string::npos) + lineStr = lineStr.substr(pos + gmText.length()); + + trim(lineStr); + pos = lineStr.find("/", 0); + + if (pos != std::string::npos) + lineStr = lineStr.substr(0, pos); + + if (!lineStr.empty()) + level = atoi(lineStr.c_str()); + + if (actorSpriteManager) + { + Being *being = actorSpriteManager->findBeingByName( + nick, Being::PLAYER); + if (being) + { + if (level > 0) + { + being->setLevel(level); + being->updateName(); + } + else + { + if (being->getLevel() > 1) + level = being->getLevel(); + } + } + } + + mOnlinePlayers.insert(nick); + + numOnline++; + switch (player_relations.getRelation(nick)) + { + case PlayerRelation::NEUTRAL: + neutral.push_back(prepareNick(nick, level, "0")); + break; + + case PlayerRelation::FRIEND: + friends.push_back(prepareNick(nick, level, "2")); + break; + + case PlayerRelation::DISREGARDED: + disregard.push_back(prepareNick(nick, level, "8")); + break; + + case PlayerRelation::IGNORED: + case PlayerRelation::ERASED: + default: + //Ignore the ignored. + break; + } + } + } + else if (lineStr.find("------------------------------") + != std::string::npos) + { + listStarted = true; + } + line = strtok(NULL, "\n"); + } + + //Set window caption + setCaption(_("Who Is Online - ") + toString(numOnline)); + + //List the online people + sort(friends.begin(), friends.end(), stringCompare); + sort(neutral.begin(), neutral.end(), stringCompare); + sort(disregard.begin(), disregard.end(), stringCompare); + bool addedFromSection(false); + for (int i = 0; i < static_cast<int>(friends.size()); i++) + { + mBrowserBox->addRow(friends.at(i)); + addedFromSection = true; + } + if (addedFromSection == true) + { + mBrowserBox->addRow("---"); + addedFromSection = false; + } + for (int i = 0; i < static_cast<int>(neutral.size()); i++) + { + mBrowserBox->addRow(neutral.at(i)); + addedFromSection = true; + } + if (addedFromSection == true && !disregard.empty()) + { + mBrowserBox->addRow("---"); + addedFromSection = false; + } + for (int i = 0; i < static_cast<int>(disregard.size()); i++) + { + mBrowserBox->addRow(disregard.at(i)); + } + + // Free the memory buffer now that we don't need it anymore + free(mMemoryBuffer); + mMemoryBuffer = 0; + + if (mScrollArea->getVerticalMaxScroll() < + mScrollArea->getVerticalScrollAmount()) + { + mScrollArea->setVerticalScrollAmount( + mScrollArea->getVerticalMaxScroll()); + } +} + +size_t WhoIsOnline::memoryWrite(void *ptr, size_t size, + size_t nmemb, FILE *stream) +{ + WhoIsOnline *wio = reinterpret_cast<WhoIsOnline *>(stream); + size_t totalMem = size * nmemb; + wio->mMemoryBuffer = static_cast<char*>(realloc(wio->mMemoryBuffer, + wio->mDownloadedBytes + totalMem)); + if (wio->mMemoryBuffer) + { + memcpy(&(wio->mMemoryBuffer[wio->mDownloadedBytes]), ptr, totalMem); + wio->mDownloadedBytes += static_cast<int>(totalMem); + } + + return totalMem; +} + +int WhoIsOnline::downloadThread(void *ptr) +{ + int attempts = 0; + WhoIsOnline *wio = reinterpret_cast<WhoIsOnline *>(ptr); + CURL *curl; + CURLcode res; + + std::string url(Client::getServerName() + "/online.txt"); + + while (attempts < 1 && !wio->mDownloadComplete) + { + curl = curl_easy_init(); + + if (curl) + { + if (!wio->mAllowUpdate) + { + curl_easy_cleanup(curl); + break; + } + wio->mDownloadedBytes = 0; + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + WhoIsOnline::memoryWrite); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr); + + curl_easy_setopt(curl, CURLOPT_USERAGENT, + strprintf(PACKAGE_EXTENDED_VERSION, branding + .getValue("appShort", "mana").c_str()).c_str()); + + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, wio->mCurlError); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ptr); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 7); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); + + struct curl_slist *pHeaders = 0; + // Make sure the resources2.txt and news.txt aren't cached, + // in order to always get the latest version. + pHeaders = curl_slist_append(pHeaders, "pragma: no-cache"); + pHeaders = + curl_slist_append(pHeaders, "Cache-Control: no-cache"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pHeaders); + + if ((res = curl_easy_perform(curl)) != 0) + { + wio->mDownloadStatus = UPDATE_ERROR; + switch (res) + { + case CURLE_COULDNT_CONNECT: + default: + std::cerr << "curl error " + << static_cast<unsigned>(res) << ": " + << wio->mCurlError << " host: " + << url.c_str() << std::endl; + break; + } + attempts++; + continue; + } + + curl_easy_cleanup(curl); + + curl_slist_free_all(pHeaders); + + // It's stored in memory, we're done + wio->mDownloadComplete = true; + } + if (!wio->mAllowUpdate) + break; + attempts++; + } + + if (!wio->mDownloadComplete) + wio->mDownloadStatus = UPDATE_ERROR; + +// wio->mThread = 0; + return 0; +} + +void WhoIsOnline::download() +{ + mDownloadComplete = true; + if (mThread && SDL_GetThreadID(mThread)) + SDL_WaitThread(mThread, NULL); + + mDownloadComplete = false; + mThread = SDL_CreateThread(WhoIsOnline::downloadThread, this); + + if (mThread == NULL) + mDownloadStatus = UPDATE_ERROR; +} + +void WhoIsOnline::logic() +{ + // Update Scroll logic + mScrollArea->logic(); + + if (!mAllowUpdate) + return; + + if (mUpdateTimer == 0) + mUpdateTimer = cur_time; + + double timeDiff = difftime(cur_time, mUpdateTimer); + int timeLimit = isVisible() ? 20 : 120; + + if (mUpdateOnlineList && timeDiff >= timeLimit + && mDownloadStatus != UPDATE_LIST) + { + if (mDownloadComplete == true) + { + setCaption(_("Who Is Online - Updating")); + mUpdateTimer = 0; + mDownloadStatus = UPDATE_LIST; + download(); + } + } + + switch (mDownloadStatus) + { + case UPDATE_ERROR: + mBrowserBox->clearRows(); + mBrowserBox->addRow("##1Failed to fetch the online list!"); + mBrowserBox->addRow(mCurlError); + mDownloadStatus = UPDATE_COMPLETE; + setCaption(_("Who Is Online - error")); + mUpdateButton->setEnabled(true); + mUpdateTimer = cur_time + 240; + break; + case UPDATE_LIST: + if (mDownloadComplete == true) + { + loadList(); + mDownloadStatus = UPDATE_COMPLETE; + mUpdateButton->setEnabled(true); + mUpdateTimer = 0; + updateSize(); + if (mOnlinePlayers.size() > 0 && chatWindow) + chatWindow->updateOnline(mOnlinePlayers); + } + break; + default: + break; + } +} + +void WhoIsOnline::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "update") + { + if (mDownloadStatus == UPDATE_COMPLETE) + { + mUpdateTimer = cur_time - 20; + if (mUpdateButton) + mUpdateButton->setEnabled(false); + setCaption(_("Who Is Online - Update")); + if (mThread && SDL_GetThreadID(mThread)) + { + SDL_WaitThread(mThread, NULL); + mThread = NULL; + } + mDownloadComplete = true; + } + } +} + +void WhoIsOnline::widgetResized(const gcn::Event &event) +{ + Window::widgetResized(event); + updateSize(); +} + +void WhoIsOnline::updateSize() +{ + if (mDownloadStatus == UPDATE_COMPLETE) + { + const gcn::Rectangle area = getChildrenArea(); + if (mUpdateButton) + mUpdateButton->setWidth(area.width - 10); + + if (mScrollArea) + mScrollArea->setSize(area.width - 10, area.height - 10 - 30); + } +} + +const std::string WhoIsOnline::prepareNick(std::string nick, int level, + std::string color) const +{ + if (mShowLevel && level > 1) + { + return strprintf("@@%s|##%s%s (%d)@@", nick.c_str(), + color.c_str(), nick.c_str(), level); + } + else + { + return strprintf("@@%s|##%s%s@@", nick.c_str(), + color.c_str(), nick.c_str()); + } +} + +void WhoIsOnline::optionChanged(const std::string &name) +{ + if (name == "updateOnlineList") + mUpdateOnlineList = config.getBoolValue("updateOnlineList"); +}
\ No newline at end of file |