summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJared Adams <jaxad0127@gmail.com>2009-10-13 09:00:01 -0600
committerJared Adams <jaxad0127@gmail.com>2009-10-13 09:00:01 -0600
commit0aed5e4eb3af56ba0bf9ceb0343c262a7427b6ba (patch)
treee63bc37eb35cd10bc012d4d54fe7d76a81a5edc1 /src
parent5987d6ff5e737d72193089418a3bf31f9f5a5c8f (diff)
downloadmana-0aed5e4eb3af56ba0bf9ceb0343c262a7427b6ba.tar.gz
mana-0aed5e4eb3af56ba0bf9ceb0343c262a7427b6ba.tar.bz2
mana-0aed5e4eb3af56ba0bf9ceb0343c262a7427b6ba.tar.xz
mana-0aed5e4eb3af56ba0bf9ceb0343c262a7427b6ba.zip
Add an asynchronous download class
And use it to download news, updates, and the server list.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/Makefile.am2
-rw-r--r--src/gui/serverdialog.cpp325
-rw-r--r--src/gui/serverdialog.h92
-rw-r--r--src/gui/updatewindow.cpp261
-rw-r--r--src/gui/updatewindow.h35
-rw-r--r--src/main.cpp13
-rw-r--r--src/net/loginhandler.h3
-rw-r--r--src/net/net.cpp10
-rw-r--r--src/net/net.h4
-rw-r--r--src/net/serverinfo.h3
11 files changed, 338 insertions, 412 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f54b6858..0b0c489e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -259,6 +259,8 @@ SET(SRCS
net/adminhandler.h
net/charhandler.h
net/chathandler.h
+ net/download.cpp
+ net/download.h
net/gamehandler.h
net/generalhandler.h
net/guildhandler.h
diff --git a/src/Makefile.am b/src/Makefile.am
index d1782b46..12ff8f94 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -208,6 +208,8 @@ tmw_SOURCES = gui/widgets/avatar.cpp \
net/adminhandler.h \
net/charhandler.h \
net/chathandler.h \
+ net/download.cpp \
+ net/download.h \
net/gamehandler.h \
net/generalhandler.h \
net/guildhandler.h \
diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp
index aaa0515b..f47f33ea 100644
--- a/src/gui/serverdialog.cpp
+++ b/src/gui/serverdialog.cpp
@@ -36,85 +36,66 @@
#include "net/net.h"
-#include "utils/xml.h"
#include "utils/gettext.h"
#include "utils/stringutils.h"
+#include "utils/xml.h"
#include <cstdlib>
#include <iostream>
#include <string>
-const short MAX_SERVERLIST = 5;
-
-int ServersListModel::getNumberOfElements()
-{
- return servers.size();
-}
-
-std::string ServersListModel::getElementAt(int elementIndex)
-{
- std::string myServer = servers.at(elementIndex).name;
- myServer += " (";
- myServer += std::string(servers.at(elementIndex).hostname);
- myServer += ":";
- myServer += toString(servers.at(elementIndex).port);
- myServer += ")";
- return myServer;
-}
+#define MAX_SERVERLIST 5
-void ServersListModel::addFirstElement(const ServerInfo &server)
+ServersListModel::ServersListModel(ServerInfos *servers, ServerDialog *parent):
+ mServers(servers),
+ mParent(parent)
{
- // Equivalent to push_front
- std::vector<ServerInfo>::iterator MyIterator = servers.begin();
- servers.insert(MyIterator, 1, server);
}
-void ServersListModel::addElement(const ServerInfo &server)
+int ServersListModel::getNumberOfElements()
{
- servers.push_back(server);
+ MutexLocker lock = mParent->lock();
+ return mServers->size();
}
-void ServersListModel::mergeElement(const ServerInfo &server)
+std::string ServersListModel::getElementAt(int elementIndex)
{
- // search through the list
- for (int i = 0; i < getNumberOfElements(); i++)
+ MutexLocker lock = mParent->lock();
+ ServerInfo server = mServers->at(elementIndex);
+ std::string myServer;
+ if (server.name.empty())
{
- // the server is already in the list, merge its properties
- if (servers[i] == server)
- {
- servers[i].name = server.name;
- return;
- }
+ myServer += server.hostname;
+ myServer += ":";
+ myServer += toString(server.port);
}
- // the server is not found, add it at the end of the list
- addElement(server);
-}
-
-bool ServersListModel::contains(const ServerInfo &server)
-{
- // search through the list
- for (int i = 0; i < getNumberOfElements(); i++)
+ else
{
- if (servers[i] == server)
- return true;
+ myServer += server.name;
+ myServer += " (";
+ myServer += server.hostname;
+ myServer += ":";
+ myServer += toString(server.port);
+ myServer += ")";
}
- return false;
+ return myServer;
}
-ServerDialog::ServerDialog(ServerInfo *serverInfo):
- Window(_("Choose Your Server")), mServerInfo(serverInfo)
+ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
+ Window(_("Choose Your Server")),
+ mDir(dir),
+ mDownloadStatus(DOWNLOADING_PREPARING),
+ mDownloadProgress(-1.0f),
+ mServers(ServerInfos()),
+ mServerInfo(serverInfo)
{
- mServerDescription = new Label(std::string());
- gcn::Label *serverLabel = new Label(_("Server:"));
- gcn::Label *portLabel = new Label(_("Port:"));
+ Label *serverLabel = new Label(_("Server:"));
+ Label *portLabel = new Label(_("Port:"));
mServerNameField = new TextField(mServerInfo->hostname);
mPortField = new TextField(toString(mServerInfo->port));
- mMostUsedServersListModel = new ServersListModel;
ServerInfo currentServer;
- ServerInfo tempServer;
-
// Add the most used servers from config if they are not in the online list
std::string currentConfig = "";
for (int i = 0; i <= MAX_SERVERLIST; i++)
@@ -129,18 +110,18 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo):
if (!currentServer.hostname.empty() && currentServer.port != 0)
{
- if (!mMostUsedServersListModel->contains(currentServer))
- mMostUsedServersListModel->addElement(currentServer);
+ mServers.push_back(currentServer);
}
}
- // load a list with online servers...
- loadServerlist();
+ mServersListModel = new ServersListModel(&mServers, this);
- mMostUsedServersList = new ListBox(mMostUsedServersListModel);
- ScrollArea *usedScroll = new ScrollArea(mMostUsedServersList);
+ mServersList = new ListBox(mServersListModel);
+ ScrollArea *usedScroll = new ScrollArea(mServersList);
usedScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
+ mDescription = new Label(std::string());
+
mQuitButton = new Button(_("Quit"), "quit", this);
mConnectButton = new Button(_("Connect"), "connect", this);
mManualEntryButton = new Button(_("Add Entry"), "addEntry", this);
@@ -151,16 +132,16 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo):
mServerNameField->addActionListener(this);
mPortField->addActionListener(this);
mManualEntryButton->addActionListener(this);
- mMostUsedServersList->addSelectionListener(this);
- mMostUsedServersList->setSelected(0);
+ mServersList->addSelectionListener(this);
+ mServersList->setSelected(0);
usedScroll->setVerticalScrollAmount(0);
- place(0, 0, mServerDescription, 2);
- place(0, 1, serverLabel);
- place(0, 2, portLabel);
- place(1, 1, mServerNameField, 3).setPadding(3);
- place(1, 2, mPortField, 3).setPadding(3);
- place(0, 3, usedScroll, 4, 5).setPadding(3);
+ place(0, 0, serverLabel);
+ place(1, 0, mServerNameField, 3).setPadding(3);
+ place(0, 1, portLabel);
+ place(1, 1, mPortField, 3).setPadding(3);
+ place(0, 2, usedScroll, 4, 5).setPadding(3);
+ place(0, 7, mDescription, 4);
place(0, 8, mManualEntryButton);
place(2, 8, mQuitButton);
place(3, 8, mConnectButton);
@@ -185,11 +166,12 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo):
}
}
+ downloadServerList();
}
ServerDialog::~ServerDialog()
{
- delete mMostUsedServersListModel;
+ delete mServersListModel;
}
void ServerDialog::action(const gcn::ActionEvent &event)
@@ -227,9 +209,9 @@ void ServerDialog::action(const gcn::ActionEvent &event)
// now add the rest of the list...
std::string currentConfig = "";
int configCount = 1;
- for (int i = 0; i < mMostUsedServersListModel->getNumberOfElements(); i++)
+ for (int i = 0; i < mServersListModel->getNumberOfElements(); i++)
{
- tempServer = mMostUsedServersListModel->getServer(i);
+ tempServer = mServersListModel->getServer(i);
// ensure, that our server will not be added twice
if (tempServer != currentServer)
@@ -262,24 +244,77 @@ void ServerDialog::action(const gcn::ActionEvent &event)
void ServerDialog::valueChanged(const gcn::SelectionEvent &event)
{
- const int index = mMostUsedServersList->getSelected();
+ const int index = mServersList->getSelected();
if (index == -1)
return;
// Update the server and post fields according to the new selection
- const ServerInfo myServer = mMostUsedServersListModel->getServer(index);
- mServerDescription->setCaption(myServer.name);
+ const ServerInfo myServer = mServersListModel->getServer(index);
+ mDescription->setCaption(myServer.name);
mServerNameField->setText(myServer.hostname);
mPortField->setText(toString(myServer.port));
setFieldsReadOnly(true);
}
-void ServerDialog::loadServerlist()
+void ServerDialog::logic()
{
- ServerInfo currentServer;
- currentServer.clear();
+ {
+ MutexLocker lock(&mMutex);
+ if (mDownloadStatus == DOWNLOADING_COMPLETE)
+ {
+ mDownloadStatus = DOWNLOADING_OVER;
+
+ mDescription->setCaption(std::string());
+ }
+ else if (mDownloadStatus == DOWNLOADING_IN_PROGRESS)
+ {
+ mDescription->setCaption(strprintf(_("Downloading server list..."
+ "%2.2f%%"),
+ mDownloadProgress * 100));
+ }
+ else if (mDownloadStatus == DOWNLOADING_IDLE)
+ {
+ mDescription->setCaption(_("Waiting for server..."));
+ }
+ else if (mDownloadStatus == DOWNLOADING_PREPARING)
+ {
+ mDescription->setCaption(_("Preparing download"));
+ }
+ }
+
+ Window::logic();
+}
+
+void ServerDialog::setFieldsReadOnly(const bool readOnly)
+{
+ if (readOnly)
+ {
+ mServerNameField->setEnabled(false);
+ mPortField->setEnabled(false);
+ mManualEntryButton->setVisible(true);
+ mDescription->setVisible(true);
+ }
+ else
+ {
+ mManualEntryButton->setVisible(false);
+
+ mDescription->setVisible(false);
+ mDescription->setCaption(std::string());
+ mServersList->setSelected(-1);
+
+ mServerNameField->setText(std::string());
+ mServerNameField->setEnabled(true);
+ mPortField->setText(toString(DEFAULT_PORT));
+ mPortField->setEnabled(true);
+
+ mServerNameField->requestFocus();
+ }
+}
+
+void ServerDialog::downloadServerList()
+{
// try to load the configuration value for the onlineServerList
std::string listFile = config.getValue("onlineServerList", "void");
// if there is no entry, try to load the file from the default updatehost
@@ -287,82 +322,116 @@ void ServerDialog::loadServerlist()
listFile = config.getValue("updatehost", "http://updates.themanaworld.org")
+ "/serverlist.xml";
- xmlDocPtr doc = xmlReadFile(listFile.c_str(), NULL, 0);
- if (doc == NULL)
- {
- logger->log("Failed to load online serverlist from %s", listFile.c_str());
- return;
- }
+ mDownload = new Net::Download(this, listFile, &downloadUpdate);
+ mDownload->setFile(mDir + "/serverlist.xml");
+ mDownload->start();
+}
+
+void ServerDialog::loadServers()
+{
+ ServerInfo currentServer;
- xmlNodePtr rootNode = xmlDocGetRootElement(doc);
- int version = XML::getProperty(rootNode, "version", 3);
+ xmlDocPtr doc = xmlReadFile((mDir + "/serverlist.xml").c_str(), NULL, 0);
- if (version != 1)
+ if (doc != NULL)
{
- logger->log("Online server list has wrong version");
- return;
- }
+ xmlNodePtr rootNode = xmlDocGetRootElement(doc);
+ int version = XML::getProperty(rootNode, "version", 3);
- for_each_xml_child_node(server, rootNode)
- {
- if (xmlStrEqual(server->name, BAD_CAST "server"))
+ if (version != 1)
{
- //check wether the version matches
- #ifdef TMWSERV_SUPPORT
- if (XML::getProperty(server, "type", "unknown") != "TMWSERV")
- continue;
- #endif
+ logger->log("Online server list has wrong version");
+ return;
+ }
- #ifdef EATHENA_SUPPORT
- if (XML::getProperty(server, "type", "unknown") != "EATHENA")
- continue;
- #endif
+ for_each_xml_child_node(server, rootNode)
+ {
+ if (xmlStrEqual(server->name, BAD_CAST "server"))
+ {
+ //check wether the version matches
+ #ifdef TMWSERV_SUPPORT
+ if (XML::getProperty(server, "type", "unknown") != "TMWSERV")
+ continue;
+ #endif
- currentServer.clear();
- currentServer.name = XML::getProperty(server, "name", std::string());
+ #ifdef EATHENA_SUPPORT
+ if (XML::getProperty(server, "type", "unknown") != "EATHENA")
+ continue;
+ #endif
- for_each_xml_child_node(subnode, server)
- {
- if (xmlStrEqual(subnode->name, BAD_CAST "connection"))
+ currentServer.clear();
+ currentServer.name = XML::getProperty(server, "name", std::string());
+
+ for_each_xml_child_node(subnode, server)
{
- currentServer.hostname = XML::getProperty(subnode, "hostname", std::string());
- currentServer.port = XML::getProperty(subnode, "port", DEFAULT_PORT);
+ if (xmlStrEqual(subnode->name, BAD_CAST "connection"))
+ {
+ currentServer.hostname = XML::getProperty(subnode, "hostname", std::string());
+ currentServer.port = XML::getProperty(subnode, "port", DEFAULT_PORT);
+ }
}
- }
- // merge the server into the local list
- mMostUsedServersListModel->mergeElement(currentServer);
+
+ MutexLocker lock(&mMutex);
+ // add the server to the local list (if it's not already present)
+ ServerInfos::iterator it;
+ bool found = false;
+ for (it = mServers.begin(); it != mServers.end(); it++)
+ {
+ if ((*it) == currentServer)
+ {
+ (*it).name = currentServer.name;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ mServers.push_back(currentServer);
+ }
}
+
+ xmlFreeDoc(doc);
}
- xmlFreeDoc(doc);
+ MutexLocker lock(&mMutex);
+ mDownloadStatus = DOWNLOADING_COMPLETE;
}
-void ServerDialog::setFieldsReadOnly(const bool readOnly)
+int ServerDialog::downloadUpdate(void *ptr, DownloadStatus status,
+ size_t total, size_t remaining)
{
- if (readOnly)
+ ServerDialog *sd = reinterpret_cast<ServerDialog*>(ptr);
+ bool finished = false;
+
+ if (status == DOWNLOAD_STATUS_COMPLETE)
{
- mServerNameField->setEnabled(false);
- mPortField->setEnabled(false);
- mManualEntryButton->setVisible(true);
- mServerDescription->setVisible(true);
+ finished = true;
}
- else
+ else if (status < 0)
{
- mManualEntryButton->setVisible(false);
+ logger->log("Error retreiving server list: %s\n",
+ sd->mDownload->getError());
- mServerDescription->setVisible(false);
- mServerDescription->setCaption(std::string());
- mMostUsedServersList->setSelected(-1);
+ finished = true;
+ }
+ else
+ {
+ float progress = (float) remaining / total;
- mServerNameField->setText(std::string());
- mServerNameField->setEnabled(true);
+ if (progress != progress) progress = 0.0f; // check for NaN
+ if (progress < 0.0f) progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway.
+ if (progress > 1.0f) progress = 1.0f;
- mPortField->setText(toString(DEFAULT_PORT));
- mPortField->setEnabled(true);
-
- mServerNameField->requestFocus();
+ MutexLocker lock(&sd->mMutex);
+ sd->mDownloadStatus = DOWNLOADING_IN_PROGRESS;
+ sd->mDownloadProgress = progress;
}
-}
+ if (finished)
+ {
+ sd->loadServers();
+ }
+ return 0;
+}
diff --git a/src/gui/serverdialog.h b/src/gui/serverdialog.h
index 4522cdad..943d69c9 100644
--- a/src/gui/serverdialog.h
+++ b/src/gui/serverdialog.h
@@ -24,10 +24,11 @@
#include "gui/widgets/window.h"
-#include "guichanfwd.h"
-
+#include "net/download.h"
#include "net/serverinfo.h"
+#include "utils/mutex.h"
+
#include <guichan/actionlistener.hpp>
#include <guichan/listmodel.hpp>
#include <guichan/selectionlistener.hpp>
@@ -35,7 +36,11 @@
#include <string>
#include <vector>
+class Button;
+class Label;
class ListBox;
+class ServerDialog;
+class TextField;
/**
* Server and Port List Model
@@ -43,6 +48,8 @@ class ListBox;
class ServersListModel : public gcn::ListModel
{
public:
+ ServersListModel(ServerInfos *servers, ServerDialog *parent);
+
/**
* Used to get number of line in the list
*/
@@ -57,36 +64,11 @@ class ServersListModel : public gcn::ListModel
* Used to get the corresponding Server struct
*/
ServerInfo getServer(int elementIndex) const
- { return servers[elementIndex]; }
-
- /**
- * Add an Element at the end of the server list
- */
- void addElement(const ServerInfo &server);
-
- /**
- * Add an Element at the end of the server list if it
- * doesn't exist yet. Otherwise overwrite its properties
- * in the list.
- *
- * @param server ServerInfo to merge into the list.
- */
- void mergeElement(const ServerInfo &server);
-
- /**
- * Add an Element at the beginning of the server list
- */
- void addFirstElement(const ServerInfo &server);
-
- /**
- * Returns wheter the given server is already in the list.
- * @param server Server to search in the list.
- * @return True, if the server is in the list, false otherwise.
- */
- bool contains(const ServerInfo &server);
+ { return mServers->at(elementIndex); }
private:
- std::vector<ServerInfo> servers;
+ ServerInfos *mServers;
+ ServerDialog *mParent;
};
/**
@@ -104,7 +86,7 @@ class ServerDialog : public Window,
*
* @see Window::Window
*/
- ServerDialog(ServerInfo *serverInfo);
+ ServerDialog(ServerInfo *serverInfo, const std::string &dir);
/**
* Destructor
@@ -121,24 +103,54 @@ class ServerDialog : public Window,
*/
void valueChanged(const gcn::SelectionEvent &event);
+ void logic();
+
+ protected:
+ friend class ServersListModel;
+ MutexLocker lock() { return MutexLocker(&mMutex); }
+
private:
/**
* Called to load a list of available server from an online xml file.
*/
- void loadServerlist();
+ void downloadServerList();
+ void loadServers();
+ static int downloadUpdate(void *ptr, DownloadStatus status,
+ size_t total, size_t remaining);
void setFieldsReadOnly(const bool readOnly);
- gcn::TextField *mServerNameField;
- gcn::TextField *mPortField;
- gcn::Label *mServerDescription;
- gcn::Button *mQuitButton;
- gcn::Button *mConnectButton;
- gcn::Button *mManualEntryButton;
+ TextField *mServerNameField;
+ TextField *mPortField;
+ Label *mDescription;
+ Button *mQuitButton;
+ Button *mConnectButton;
+ Button *mManualEntryButton;
+
+ ListBox *mServersList;
+ ServersListModel *mServersListModel;
+
+ const std::string &mDir;
+
+ enum ServerDialogDownloadStatus
+ {
+ DOWNLOADING_ERROR,
+ DOWNLOADING_PREPARING,
+ DOWNLOADING_IDLE,
+ DOWNLOADING_IN_PROGRESS,
+ DOWNLOADING_COMPLETE,
+ DOWNLOADING_OVER
+ };
+
+ /** Status of the current download. */
+ ServerDialogDownloadStatus mDownloadStatus;
+
+ Net::Download *mDownload;
- ListBox *mMostUsedServersList;
- ServersListModel *mMostUsedServersListModel;
+ Mutex mMutex;
+ float mDownloadProgress;
+ ServerInfos mServers;
ServerInfo *mServerInfo;
};
diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp
index b39ee8c2..14ebe6b1 100644
--- a/src/gui/updatewindow.cpp
+++ b/src/gui/updatewindow.cpp
@@ -32,37 +32,14 @@
#include "log.h"
#include "main.h"
+#include "net/download.h"
+
#include "resources/resourcemanager.h"
#include "utils/gettext.h"
#include "utils/stringutils.h"
#include <iostream>
-#include <SDL.h>
-#include <SDL_thread.h>
-#include <zlib.h>
-
-#include <curl/curl.h>
-
-/**
- * Calculates the Alder-32 checksum for the given file.
- */
-static unsigned long fadler32(FILE *file)
-{
- // Obtain file size
- fseek(file, 0, SEEK_END);
- long fileSize = ftell(file);
- rewind(file);
-
- // Calculate Adler-32 checksum
- char *buffer = (char*) malloc(fileSize);
- const size_t read = fread(buffer, 1, fileSize, file);
- unsigned long adler = adler32(0L, Z_NULL, 0);
- adler = adler32(adler, (Bytef*) buffer, read);
- free(buffer);
-
- return adler;
-}
/**
* Load the given file into a vector of strings.
@@ -89,22 +66,20 @@ std::vector<std::string> loadTextFile(const std::string &fileName)
UpdaterWindow::UpdaterWindow(const std::string &updateHost,
const std::string &updatesDir):
Window(_("Updating...")),
- mThread(NULL),
mDownloadStatus(UPDATE_NEWS),
mUpdateHost(updateHost),
mUpdatesDir(updatesDir),
mCurrentFile("news.txt"),
+ mDownloadProgress(0.0f),
mCurrentChecksum(0),
mStoreInMemory(true),
mDownloadComplete(true),
mUserCancel(false),
mDownloadedBytes(0),
mMemoryBuffer(NULL),
- mCurlError(new char[CURL_ERROR_SIZE]),
+ mDownload(NULL),
mLineIndex(0)
{
- mCurlError[0] = 0;
-
mBrowserBox = new BrowserBox;
mScrollArea = new ScrollArea(mBrowserBox);
mLabel = new Label(_("Connecting..."));
@@ -112,6 +87,7 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost,
mCancelButton = new Button(_("Cancel"), "cancel", this);
mPlayButton = new Button(_("Play"), "play", this);
+ mProgressBar->setSmoothProgress(false);
mBrowserBox->setOpaque(false);
mPlayButton->setEnabled(false);
@@ -139,26 +115,20 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost,
UpdaterWindow::~UpdaterWindow()
{
- if (mThread)
- SDL_WaitThread(mThread, NULL);
-
free(mMemoryBuffer);
-
- // Remove possibly leftover temporary download
- ::remove((mUpdatesDir + "/download.temp").c_str());
-
- delete[] mCurlError;
}
void UpdaterWindow::setProgress(float p)
{
- mProgressBar->setProgress(p);
+ // Do delayed progress bar update, since Guichan isn't thread-safe
+ MutexLocker lock(&mDownloadMutex);
+ mDownloadProgress = p;
}
void UpdaterWindow::setLabel(const std::string &str)
{
// Do delayed label text update, since Guichan isn't thread-safe
- MutexLocker lock(&mLabelMutex);
+ MutexLocker lock(&mDownloadMutex);
mNewLabelCaption = str;
}
@@ -178,6 +148,7 @@ void UpdaterWindow::action(const gcn::ActionEvent &event)
// Skip the updating process
if (mDownloadStatus != UPDATE_COMPLETE)
{
+ mDownload->cancel();
mDownloadStatus = UPDATE_ERROR;
}
}
@@ -216,12 +187,23 @@ void UpdaterWindow::loadNews()
mScrollArea->setVerticalScrollAmount(0);
}
-int UpdaterWindow::updateProgress(void *ptr,
- double dt, double dn, double ut, double un)
+int UpdaterWindow::updateProgress(void *ptr, DownloadStatus status,
+ size_t dt, size_t dn)
{
- float progress = dn / dt;
UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(ptr);
+ if (status == DOWNLOAD_STATUS_COMPLETE)
+ {
+ uw->mDownloadComplete = true;
+ }
+ else if (status == DOWNLOAD_STATUS_ERROR ||
+ status == DOWNLOAD_STATUS_CANCELLED)
+ {
+ uw->mDownloadStatus = UPDATE_ERROR;
+ }
+
+ float progress = (float) dn / dt;
+
if (progress != progress) progress = 0.0f; // check for NaN
if (progress < 0.0f) progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway.
if (progress > 1.0f) progress = 1.0f;
@@ -239,7 +221,7 @@ int UpdaterWindow::updateProgress(void *ptr,
return 0;
}
-size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
+size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, void *stream)
{
UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(stream);
size_t totalMem = size * nmemb;
@@ -254,162 +236,37 @@ size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, FILE *st
return totalMem;
}
-int UpdaterWindow::downloadThread(void *ptr)
+void UpdaterWindow::download()
{
- int attempts = 0;
- UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(ptr);
- CURL *curl;
- CURLcode res;
- std::string outFilename;
- std::string url(uw->mUpdateHost + "/" + uw->mCurrentFile);
+ mDownload = new Net::Download(this, mUpdateHost + "/" + mCurrentFile,
+ updateProgress);
- while (attempts < 3 && !uw->mDownloadComplete)
+ if (mStoreInMemory)
{
- FILE *outfile = NULL;
- FILE *newfile = NULL;
- uw->setLabel(uw->mCurrentFile + " (0%)");
-
- curl = curl_easy_init();
-
- if (curl)
+ mDownload->setWriteFunction(UpdaterWindow::memoryWrite);
+ }
+ else
+ {
+ if (mDownloadStatus == UPDATE_RESOURCES)
{
- logger->log("Downloading: %s", url.c_str());
-
- if (uw->mStoreInMemory)
- {
- uw->mDownloadedBytes = 0;
- curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
- UpdaterWindow::memoryWrite);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
- }
- else
- {
- outFilename = uw->mUpdatesDir + "/download.temp";
- outfile = fopen(outFilename.c_str(), "w+b");
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
- }
-
-#ifdef PACKAGE_VERSION
- curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMW/" PACKAGE_VERSION);
-#else
- curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMW");
-#endif
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, uw->mCurlError);
- curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
- curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
- curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,
- UpdaterWindow::updateProgress);
- curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ptr);
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
-
- struct curl_slist *pHeaders = NULL;
- if (uw->mDownloadStatus != UPDATE_RESOURCES)
- {
- // 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)
- {
- uw->mDownloadStatus = UPDATE_ERROR;
- switch (res)
- {
- case CURLE_COULDNT_CONNECT:
- default:
- logger->log("curl error %d: %s host: %s",
- res, uw->mCurlError, url.c_str());
- break;
- }
-
- if (!uw->mStoreInMemory)
- {
- fclose(outfile);
- ::remove(outFilename.c_str());
- }
- attempts++;
- continue;
- }
-
- curl_easy_cleanup(curl);
-
- if (uw->mDownloadStatus != UPDATE_RESOURCES)
- {
- curl_slist_free_all(pHeaders);
- }
-
- if (!uw->mStoreInMemory)
- {
- // Don't check resources2.txt checksum
- if (uw->mDownloadStatus == UPDATE_RESOURCES)
- {
- unsigned long adler = fadler32(outfile);
-
- if (uw->mCurrentChecksum != adler)
- {
- fclose(outfile);
-
- // Remove the corrupted file
- ::remove(outFilename.c_str());
- logger->log(
- "Checksum for file %s failed: (%lx/%lx)",
- uw->mCurrentFile.c_str(),
- adler, uw->mCurrentChecksum);
- attempts++;
- continue; // Bail out here to avoid the renaming
- }
- }
- fclose(outfile);
-
- // Give the file the proper name
- const std::string newName =
- uw->mUpdatesDir + "/" + uw->mCurrentFile;
-
- // Any existing file with this name is deleted first, otherwise
- // the rename will fail on Windows.
- ::remove(newName.c_str());
- ::rename(outFilename.c_str(), newName.c_str());
-
- // Check if we can open it and no errors were encountered
- // during renaming
- newfile = fopen(newName.c_str(), "rb");
- if (newfile)
- {
- fclose(newfile);
- uw->mDownloadComplete = true;
- }
- }
- else
- {
- // It's stored in memory, we're done
- uw->mDownloadComplete = true;
- }
+ mDownload->setFile(mUpdatesDir + "/" + mCurrentFile, mCurrentChecksum);
+ }
+ else
+ {
+ mDownload->setFile(mUpdatesDir + "/" + mCurrentFile);
}
- attempts++;
}
- if (!uw->mDownloadComplete) {
- uw->mDownloadStatus = UPDATE_ERROR;
+ if (mDownloadStatus != UPDATE_RESOURCES)
+ {
+ mDownload->noCache();
}
- return 0;
-}
-
-void UpdaterWindow::download()
-{
+ setLabel(mCurrentFile + " (0%)");
mDownloadComplete = false;
- mThread = SDL_CreateThread(UpdaterWindow::downloadThread, this);
- if (!mThread)
- {
- logger->log("Unable to create mThread");
- mDownloadStatus = UPDATE_ERROR;
- }
+ // TODO: check return
+ mDownload->start();
}
void UpdaterWindow::logic()
@@ -419,31 +276,22 @@ void UpdaterWindow::logic()
// Synchronize label caption when necessary
{
- MutexLocker lock(&mLabelMutex);
+ MutexLocker lock(&mDownloadMutex);
if (mLabel->getCaption() != mNewLabelCaption)
{
mLabel->setCaption(mNewLabelCaption);
mLabel->adjustSize();
}
+
+ mProgressBar->setProgress(mDownloadProgress);
}
+ std::string filename = mUpdatesDir + "/" + mCurrentFile;
+
switch (mDownloadStatus)
{
case UPDATE_ERROR:
- if (mThread)
- {
- if (mUserCancel) {
- // Kill the thread, because user has canceled
- SDL_KillThread(mThread);
- // Set the flag to false again
- mUserCancel = false;
- }
- else {
- SDL_WaitThread(mThread, NULL);
- }
- mThread = NULL;
- }
// TODO: Only send complete sentences to gettext
mBrowserBox->addRow("");
mBrowserBox->addRow(_("##1 The update process is incomplete."));
@@ -451,7 +299,8 @@ void UpdaterWindow::logic()
mBrowserBox->addRow(_("##1 It is strongly recommended that"));
// TRANSLATORS: Begins "It is strongly recommended that".
mBrowserBox->addRow(_("##1 you try again later."));
- mBrowserBox->addRow(mCurlError);
+
+ mBrowserBox->addRow(mDownload->getError());
mScrollArea->setVerticalScrollAmount(
mScrollArea->getVerticalMaxScroll());
mDownloadStatus = UPDATE_COMPLETE;
@@ -479,12 +328,6 @@ void UpdaterWindow::logic()
case UPDATE_RESOURCES:
if (mDownloadComplete)
{
- if (mThread)
- {
- SDL_WaitThread(mThread, NULL);
- mThread = NULL;
- }
-
if (mLineIndex < mLines.size())
{
std::stringstream line(mLines[mLineIndex]);
diff --git a/src/gui/updatewindow.h b/src/gui/updatewindow.h
index 65a934dc..6e738fca 100644
--- a/src/gui/updatewindow.h
+++ b/src/gui/updatewindow.h
@@ -24,6 +24,8 @@
#include "gui/widgets/window.h"
+#include "net/download.h"
+
#include "utils/mutex.h"
#include <guichan/actionlistener.hpp>
@@ -36,8 +38,6 @@ class Button;
class ProgressBar;
class ScrollArea;
-struct SDL_Thread;
-
/**
* Update progress window GUI
*
@@ -92,23 +92,18 @@ private:
void download();
/**
- * The thread function that download the files.
+ * A download callback for progress updates.
*/
- static int downloadThread(void *ptr);
-
- /**
- * A libcurl callback for progress updates.
- */
- static int updateProgress(void *ptr,
- double dt, double dn, double ut, double un);
+ static int updateProgress(void *ptr, DownloadStatus status,
+ size_t dt, size_t dn);
/**
* A libcurl callback for writing to memory.
*/
static size_t memoryWrite(void *ptr, size_t size, size_t nmemb,
- FILE *stream);
+ void *stream);
- enum DownloadStatus
+ enum UpdateDownloadStatus
{
UPDATE_ERROR,
UPDATE_IDLE,
@@ -118,11 +113,8 @@ private:
UPDATE_RESOURCES
};
- /** A thread that use libcurl to download updates. */
- SDL_Thread *mThread;
-
/** Status of the current download. */
- DownloadStatus mDownloadStatus;
+ UpdateDownloadStatus mDownloadStatus;
/** Host where we get the updated files. */
std::string mUpdateHost;
@@ -136,8 +128,11 @@ private:
/** The new label caption to be set in the logic method. */
std::string mNewLabelCaption;
- /** The mutex used to guard access to mNewLabelCaption. */
- Mutex mLabelMutex;
+ /** The new progress value to be set in the logic method. */
+ float mDownloadProgress;
+
+ /** The mutex used to guard access to mNewLabelCaption and mDownloadProgress. */
+ Mutex mDownloadMutex;
/** The Adler32 checksum of the file currently downloading. */
unsigned long mCurrentChecksum;
@@ -157,8 +152,8 @@ private:
/** Buffer for files downloaded to memory. */
char *mMemoryBuffer;
- /** Buffer to handler human readable error provided by curl. */
- char *mCurlError;
+ /** Download handle. */
+ Net::Download *mDownload;
/** List of files to download. */
std::vector<std::string> mLines;
diff --git a/src/main.cpp b/src/main.cpp
index 0df822b9..c05ce193 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -926,7 +926,7 @@ int main(int argc, char *argv[])
loadUpdates();
}
- printf("State change: %d to %d\n", oldstate, state);
+ //printf("State change: %d to %d\n", oldstate, state);
oldstate = state;
@@ -945,15 +945,16 @@ int main(int argc, char *argv[])
case STATE_CHOOSE_SERVER:
logger->log("State: CHOOSE SERVER");
- // Don't allow an alpha opacity
- // lower than the default value
- SkinLoader::instance()->setMinimumOpacity(0.8f);
-
// Allow changing this using a server choice dialog
// We show the dialog box only if the command-line
// options weren't set.
if (options.serverName.empty() && options.serverPort == 0) {
- currentDialog = new ServerDialog(&currentServer);
+ // Don't allow an alpha opacity
+ // lower than the default value
+ SkinLoader::instance()->setMinimumOpacity(0.8f);
+
+ currentDialog = new ServerDialog(&currentServer,
+ homeDir);
} else {
state = STATE_CONNECT_SERVER;
diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h
index 12a0c26f..2ac34c1e 100644
--- a/src/net/loginhandler.h
+++ b/src/net/loginhandler.h
@@ -22,8 +22,7 @@
#ifndef LOGINHANDLER_H
#define LOGINHANDLER_H
-#include "logindata.h"
-
+#include "net/logindata.h"
#include "net/serverinfo.h"
#include "net/worldinfo.h"
diff --git a/src/net/net.cpp b/src/net/net.cpp
index 1d771f0a..5eccccf9 100644
--- a/src/net/net.cpp
+++ b/src/net/net.cpp
@@ -125,7 +125,7 @@ Net::TradeHandler *Net::getTradeHandler()
namespace Net
{
bool networkLoaded = false;
-}
+} // namespace Net
void Net::connectToServer(const ServerInfo &server)
{
@@ -134,7 +134,7 @@ void Net::connectToServer(const ServerInfo &server)
if (networkLoaded)
{
- Net::getGeneralHandler()->reload();
+ getGeneralHandler()->reload();
}
else
{
@@ -145,11 +145,11 @@ void Net::connectToServer(const ServerInfo &server)
#endif
}
- Net::getGeneralHandler()->load();
+ getGeneralHandler()->load();
networkLoaded = true;
- Net::getLoginHandler()->setServer(server);
+ getLoginHandler()->setServer(server);
- Net::getLoginHandler()->connect();
+ getLoginHandler()->connect();
}
diff --git a/src/net/net.h b/src/net/net.h
index 5b0c92f4..7a49121d 100644
--- a/src/net/net.h
+++ b/src/net/net.h
@@ -19,11 +19,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "net/serverinfo.h"
-
#ifndef NET_H
#define NET_H
+class ServerInfo;
+
namespace Net {
class AdminHandler;
diff --git a/src/net/serverinfo.h b/src/net/serverinfo.h
index f410e516..4f68c6d6 100644
--- a/src/net/serverinfo.h
+++ b/src/net/serverinfo.h
@@ -23,6 +23,7 @@
#define SERVERINFO_H
#include <string>
+#include <vector>
class ServerInfo
{
@@ -49,4 +50,6 @@ public:
}
};
+typedef std::vector<ServerInfo> ServerInfos;
+
#endif // SERVERINFO_H