summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui/serverdialog.cpp233
-rw-r--r--src/gui/serverdialog.h30
-rw-r--r--src/gui/updaterwindow.cpp372
-rw-r--r--src/gui/updaterwindow.h88
-rw-r--r--src/net/download.cpp104
-rw-r--r--src/net/download.h82
-rw-r--r--src/net/tmwa/tradehandler.cpp6
-rw-r--r--src/utils/mutex.h39
8 files changed, 422 insertions, 532 deletions
diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp
index 90469662..4c54f2ce 100644
--- a/src/gui/serverdialog.cpp
+++ b/src/gui/serverdialog.cpp
@@ -43,7 +43,6 @@
#include "utils/gettext.h"
#include "utils/stringutils.h"
-#include "utils/xml.h"
#include <guichan/font.hpp>
@@ -59,13 +58,11 @@ ServersListModel::ServersListModel(ServerInfos *servers, ServerDialog *parent):
int ServersListModel::getNumberOfElements()
{
- MutexLocker lock(mParent->getMutex());
return mServers->size();
}
std::string ServersListModel::getElementAt(int elementIndex)
{
- MutexLocker lock(mParent->getMutex());
const ServerInfo &server = mServers->at(elementIndex);
std::string myServer;
myServer += server.hostname;
@@ -162,7 +159,6 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
loadCustomServers();
mServersListModel = new ServersListModel(&mServers, this);
-
mServersList = new ServersListBox(mServersListModel);
auto *usedScroll = new ScrollArea(mServersList);
@@ -225,14 +221,6 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
ServerDialog::~ServerDialog()
{
- if (mDownload)
- {
- mDownload->cancel();
-
- // Make sure thread is gone before deleting the ServersListModel
- mDownload.reset();
- }
-
delete mServersListModel;
}
@@ -345,7 +333,6 @@ void ServerDialog::valueChanged(const gcn::SelectionEvent &)
// Update the server and post fields according to the new selection
const ServerInfo &myServer = mServersListModel->getServer(index);
mDescription->setCaption(myServer.description);
-
mDeleteButton->setEnabled(myServer.save);
mModifyButton->setEnabled(myServer.save);
}
@@ -362,36 +349,42 @@ void ServerDialog::mouseClicked(gcn::MouseEvent &mouseEvent)
void ServerDialog::logic()
{
- {
- MutexLocker lock(&mMutex);
- if (mDownloadStatus == DOWNLOADING_COMPLETE)
- {
- mDownloadStatus = DOWNLOADING_OVER;
+ Window::logic();
- mDescription->setCaption(mServers[0].description);
- mDownloadText->setCaption(std::string());
- }
- else if (mDownloadStatus == DOWNLOADING_IN_PROGRESS)
- {
- mDownloadText->setCaption(strprintf(_("Downloading server list..."
- "%2.0f%%"),
- mDownloadProgress * 100));
- }
- else if (mDownloadStatus == DOWNLOADING_IDLE)
- {
- mDownloadText->setCaption(_("Waiting for server..."));
- }
- else if (mDownloadStatus == DOWNLOADING_PREPARING)
+ if (mDownloadDone)
+ return;
+
+ auto state = mDownload->getState();
+
+ switch (state.status) {
+ case DownloadStatus::IN_PROGRESS:
+ mDownloadText->setCaption(strprintf(_("Downloading server list..."
+ "%2.0f%%"),
+ state.progress * 100));
+ break;
+
+ case DownloadStatus::CANCELED:
+ case DownloadStatus::ERROR:
+ mDownloadDone = true;
+ logger->log("Error retrieving server list: %s", mDownload->getError());
+ mDownloadText->setCaption(_("Error retrieving server list!"));
+ break;
+
+ case DownloadStatus::COMPLETE:
+ mDownloadDone = true;
+ loadServers();
+
+ if (mServers.empty())
{
- mDownloadText->setCaption(_("Preparing download"));
+ mDownloadText->setCaption(_("No servers found!"));
}
- else if (mDownloadStatus == DOWNLOADING_ERROR)
+ else
{
- mDownloadText->setCaption(_("Error retreiving server list!"));
+ mDownloadText->setCaption(std::string());
+ mDescription->setCaption(mServers[0].description);
}
+ break;
}
-
- Window::logic();
}
void ServerDialog::downloadServerList()
@@ -406,8 +399,7 @@ void ServerDialog::downloadServerList()
if (listFile.empty())
listFile = "https://www.manasource.org/serverlist.xml";
- mDownload = std::make_unique<Net::Download>(this, listFile,
- &ServerDialog::downloadUpdate);
+ mDownload = std::make_unique<Net::Download>(listFile);
mDownload->setFile(mDir + "/serverlist.xml");
mDownload->start();
}
@@ -433,89 +425,92 @@ void ServerDialog::loadServers()
for (auto serverNode : rootNode.children())
{
- if (serverNode.name() != "server")
- continue;
+ if (serverNode.name() == "server")
+ loadServer(serverNode);
+ }
+}
- ServerInfo server;
+void ServerDialog::loadServer(XML::Node serverNode)
+{
+ ServerInfo server;
- std::string type = serverNode.getProperty("type", "unknown");
+ std::string type = serverNode.getProperty("type", "unknown");
- server.type = ServerInfo::parseType(type);
+ server.type = ServerInfo::parseType(type);
- // Ignore unknown server types
- if (server.type == ServerType::UNKNOWN
+ // Ignore unknown server types
+ if (server.type == ServerType::UNKNOWN
#ifndef MANASERV_SUPPORT
- || server.type == ServerType::MANASERV
+ || server.type == ServerType::MANASERV
#endif
)
- {
- logger->log("Ignoring server entry with unknown type: %s",
- type.c_str());
- continue;
- }
+ {
+ logger->log("Ignoring server entry with unknown type: %s",
+ type.c_str());
+ return;
+ }
- server.name = serverNode.getProperty("name", std::string());
+ server.name = serverNode.getProperty("name", std::string());
- std::string version = serverNode.getProperty("minimumVersion",
- std::string());
+ std::string version = serverNode.getProperty("minimumVersion",
+ std::string());
- bool meetsMinimumVersion = compareStrI(version, PACKAGE_VERSION) <= 0;
+ bool meetsMinimumVersion = compareStrI(version, PACKAGE_VERSION) <= 0;
- // For display in the list
- if (meetsMinimumVersion)
- version.clear();
- else if (version.empty())
- version = _("requires a newer version");
- else
- version = strprintf(_("requires v%s"), version.c_str());
+ // For display in the list
+ if (meetsMinimumVersion)
+ version.clear();
+ else if (version.empty())
+ version = _("requires a newer version");
+ else
+ version = strprintf(_("requires v%s"), version.c_str());
- for (auto subNode : serverNode.children())
+ for (auto subNode : serverNode.children())
+ {
+ if (subNode.name() == "connection")
{
- if (subNode.name() == "connection")
- {
- server.hostname = subNode.getProperty("hostname", std::string());
- server.port = subNode.getProperty("port", 0);
- if (server.port == 0)
- {
- // If no port is given, use the default for the given type
- server.port = ServerInfo::defaultPortForServerType(server.type);
- }
- }
- else if (subNode.name() == "description")
- {
- server.description = subNode.textContent();
- }
- else if (subNode.name() == "persistentIp")
+ server.hostname = subNode.getProperty("hostname", std::string());
+ server.port = subNode.getProperty("port", 0);
+ if (server.port == 0)
{
- const auto text = subNode.textContent();
- server.persistentIp = text == "1" || text == "true";
+ // If no port is given, use the default for the given type
+ server.port = ServerInfo::defaultPortForServerType(server.type);
}
}
+ else if (subNode.name() == "description")
+ {
+ server.description = subNode.textContent();
+ }
+ else if (subNode.name() == "persistentIp")
+ {
+ const auto text = subNode.textContent();
+ server.persistentIp = text == "1" || text == "true";
+ }
+ }
- server.version.first = gui->getFont()->getWidth(version);
- server.version.second = version;
+ server.version.first = gui->getFont()->getWidth(version);
+ server.version.second = version;
- // Add the server to the local list if it's not already present
- bool found = false;
- int i = 0;
- for (auto &s : mServers)
+ // Add the server to the local list if it's not already present
+ bool found = false;
+ int i = 0;
+ for (auto &s : mServers)
+ {
+ if (s == server)
{
- if (s == server)
- {
- // Use the name listed in the server list
- s.name = server.name;
- s.version = server.version;
- s.description = server.description;
- mServersListModel->setVersionString(i, version);
- found = true;
- break;
- }
- ++i;
+ // Use the name listed in the server list
+ s.name = server.name;
+ s.version = server.version;
+ s.description = server.description;
+ mServersListModel->setVersionString(i, version);
+ found = true;
+ break;
}
-
- if (!found)
- mServers.push_back(server);
+ ++i;
}
+
+ if (!found)
+ mServers.push_back(server);
}
void ServerDialog::loadCustomServers()
@@ -558,38 +553,6 @@ void ServerDialog::saveCustomServers(const ServerInfo &currentServer, int index)
// Restore the correct description
if (index < 0)
index = 0;
- mDescription->setCaption(mServers[index].description);
-}
-
-int ServerDialog::downloadUpdate(void *ptr, DownloadStatus status,
- size_t dltotal, size_t dlnow)
-{
- if (status == DOWNLOAD_STATUS_CANCELLED)
- return -1;
-
- auto *sd = reinterpret_cast<ServerDialog*>(ptr);
- MutexLocker lock(&sd->mMutex);
-
- if (status == DOWNLOAD_STATUS_COMPLETE)
- {
- sd->loadServers();
- sd->mDownloadStatus = DOWNLOADING_COMPLETE;
- }
- else if (status < 0)
- {
- logger->log("Error retreiving server list: %s",
- sd->mDownload->getError());
- sd->mDownloadStatus = DOWNLOADING_ERROR;
- }
- else
- {
- float progress = 0.0f;
- if (dltotal > 0)
- progress = static_cast<float>(dlnow) / dltotal;
-
- sd->mDownloadStatus = DOWNLOADING_IN_PROGRESS;
- sd->mDownloadProgress = progress;
- }
-
- return 0;
+ if (static_cast<size_t>(index) < mServers.size())
+ mDescription->setCaption(mServers[index].description);
}
diff --git a/src/gui/serverdialog.h b/src/gui/serverdialog.h
index bf2a9512..e0d74006 100644
--- a/src/gui/serverdialog.h
+++ b/src/gui/serverdialog.h
@@ -25,8 +25,7 @@
#include "net/download.h"
#include "net/serverinfo.h"
-
-#include "utils/mutex.h"
+#include "utils/xml.h"
#include <guichan/actionlistener.hpp>
#include <guichan/keylistener.hpp>
@@ -90,7 +89,6 @@ class ServerDialog : public Window,
{
public:
ServerDialog(ServerInfo *serverInfo, const std::string &dir);
-
~ServerDialog() override;
/**
@@ -110,10 +108,8 @@ class ServerDialog : public Window,
void logic() override;
protected:
- friend class ServersListModel;
- Mutex *getMutex() { return &mMutex; }
-
friend class CustomServerDialog;
+
/**
* Saves the new server entry in the custom server list.
* Removes the given entry when the serverInfo is empty.
@@ -128,12 +124,10 @@ class ServerDialog : public Window,
*/
void downloadServerList();
void loadServers();
+ void loadServer(XML::Node serverNode);
void loadCustomServers();
- static int downloadUpdate(void *ptr, DownloadStatus status,
- size_t dltotal, size_t dlnow);
-
Label *mDescription;
Button *mQuitButton;
Button *mConnectButton;
@@ -146,25 +140,9 @@ class ServerDialog : public Window,
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 = DOWNLOADING_PREPARING;
-
std::unique_ptr<Net::Download> mDownload;
+ bool mDownloadDone = false;
Label *mDownloadText;
-
- Mutex mMutex;
- float mDownloadProgress = 0.0f;
-
ServerInfos mServers;
ServerInfo *mServerInfo;
};
diff --git a/src/gui/updaterwindow.cpp b/src/gui/updaterwindow.cpp
index 852d3ade..34169195 100644
--- a/src/gui/updaterwindow.cpp
+++ b/src/gui/updaterwindow.cpp
@@ -123,7 +123,6 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost,
Window(_("Updating...")),
mUpdateHost(updateHost),
mUpdatesDir(updatesDir),
- mCurrentFile("news.txt"),
mLoadUpdates(applyUpdates),
mLinkHandler(std::make_unique<ItemLinkHandler>(this))
{
@@ -161,41 +160,22 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost,
setVisible(true);
mCancelButton->requestFocus();
- // Try to download the updates list
- download();
+ startDownload("news.txt", true);
}
UpdaterWindow::~UpdaterWindow()
{
if (mLoadUpdates)
loadUpdates();
-
- if (mDownload)
- {
- mDownload->cancel();
-
- // Make sure thread is gone before freeing the memory buffer
- mDownload.reset();
- }
-
- free(mMemoryBuffer);
-}
-
-void UpdaterWindow::setProgress(float progress)
-{
- // Do delayed progress bar update, since Guichan isn't thread-safe
- MutexLocker lock(&mDownloadMutex);
- mDownloadProgress = progress;
}
void UpdaterWindow::setLabel(const std::string &str)
{
- // Do delayed label text update, since Guichan isn't thread-safe
- MutexLocker lock(&mDownloadMutex);
- mNewLabelCaption = str;
+ mLabel->setCaption(str);
+ mLabel->adjustSize();
}
-void UpdaterWindow::enable()
+void UpdaterWindow::enablePlay()
{
mCancelButton->setEnabled(false);
mPlayButton->setEnabled(true);
@@ -205,20 +185,9 @@ void UpdaterWindow::enable()
void UpdaterWindow::action(const gcn::ActionEvent &event)
{
if (event.getId() == "cancel")
- {
- // Register the user cancel
- mUserCancel = true;
- // Skip the updating process
- if (mDownloadStatus != UPDATE_COMPLETE)
- {
- mDownload->cancel();
- mDownloadStatus = UPDATE_ERROR;
- }
- }
+ cancel();
else if (event.getId() == "play")
- {
- Client::setState(STATE_LOAD_DATA);
- }
+ play();
}
void UpdaterWindow::keyPressed(gcn::KeyEvent &keyEvent)
@@ -227,114 +196,58 @@ void UpdaterWindow::keyPressed(gcn::KeyEvent &keyEvent)
if (key.getValue() == Key::ESCAPE)
{
- action(gcn::ActionEvent(nullptr, mCancelButton->getActionEventId()));
- Client::setState(STATE_WORLD_SELECT);
+ if (!cancel())
+ {
+ mLoadUpdates = false;
+ Client::setState(STATE_WORLD_SELECT);
+ }
}
else if (key.getValue() == Key::ENTER)
{
- if (mDownloadStatus == UPDATE_COMPLETE ||
- mDownloadStatus == UPDATE_ERROR)
- {
- action(gcn::ActionEvent(nullptr, mPlayButton->getActionEventId()));
- }
- else
- {
- action(gcn::ActionEvent(nullptr, mCancelButton->getActionEventId()));
- }
+ play();
}
}
-void UpdaterWindow::loadNews()
+bool UpdaterWindow::cancel()
{
- if (!mMemoryBuffer)
+ // Skip the updating process
+ if (mDialogState != DialogState::DONE)
{
- logger->log("Couldn't load news");
- return;
+ mDownload->cancel();
+ return true;
}
-
- mBrowserBox->clearRows();
- mBrowserBox->addRows(std::string_view(mMemoryBuffer, mDownloadedBytes));
-
- // Free the memory buffer now that we don't need it anymore
- free(mMemoryBuffer);
- mMemoryBuffer = nullptr;
-
- mScrollArea->setVerticalScrollAmount(0);
+ return false;
}
-int UpdaterWindow::updateProgress(void *ptr, DownloadStatus status,
- size_t dltotal, size_t dlnow)
+void UpdaterWindow::play()
{
- auto *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 = 0.0f;
- if (dltotal > 0)
- progress = static_cast<float>(dlnow) / dltotal;
-
- uw->setLabel(
- uw->mCurrentFile + " (" + toString((int) (progress * 100)) + "%)");
- uw->setProgress(progress);
-
- if (Client::getState() != STATE_UPDATE)
- {
- // If the action was canceled return an error code to stop the mThread
- return -1;
- }
-
- return 0;
+ if (mPlayButton->isEnabled())
+ Client::setState(STATE_LOAD_DATA);
}
-size_t UpdaterWindow::memoryWrite(char *ptr, size_t size, size_t nmemb, void *stream)
+void UpdaterWindow::loadNews()
{
- auto *uw = reinterpret_cast<UpdaterWindow *>(stream);
- const size_t totalMem = size * nmemb;
- uw->mMemoryBuffer = (char*) realloc(uw->mMemoryBuffer,
- uw->mDownloadedBytes + totalMem);
- if (uw->mMemoryBuffer)
- {
- memcpy(uw->mMemoryBuffer + uw->mDownloadedBytes, ptr, totalMem);
- uw->mDownloadedBytes += totalMem;
- }
+ mBrowserBox->clearRows();
+ mBrowserBox->addRows(mDownload->getBuffer());
- return totalMem;
+ mScrollArea->setVerticalScrollAmount(0);
}
-void UpdaterWindow::download()
+void UpdaterWindow::startDownload(const std::string &fileName,
+ bool storeInMemory,
+ std::optional<unsigned long> adler32)
{
- mDownload = std::make_unique<Net::Download>(this,
- mUpdateHost + "/" + mCurrentFile,
- &UpdaterWindow::updateProgress);
+ mDownload = std::make_unique<Net::Download>(mUpdateHost + "/" + fileName);
+ mCurrentFile = fileName;
- if (mStoreInMemory)
- {
- mDownload->setWriteFunction(UpdaterWindow::memoryWrite);
- }
+ if (storeInMemory)
+ mDownload->setUseBuffer();
else
- {
- std::optional<unsigned long> adler32;
- if (mDownloadStatus == UPDATE_RESOURCES)
- adler32 = mCurrentChecksum;
-
- mDownload->setFile(mUpdatesDir + "/" + mCurrentFile, adler32);
- }
+ mDownload->setFile(mUpdatesDir + "/" + fileName, adler32);
- if (mDownloadStatus != UPDATE_RESOURCES)
+ if (mDialogState != DialogState::DOWNLOAD_RESOURCES)
mDownload->noCache();
- setLabel(mCurrentFile + " (0%)");
- mDownloadComplete = false;
-
- // TODO: check return
mDownload->start();
}
@@ -359,127 +272,132 @@ void UpdaterWindow::loadUpdates()
void UpdaterWindow::logic()
{
- const std::string xmlUpdateFile = "resources.xml";
- const std::string txtUpdateFile = "resources2.txt";
+ Window::logic();
- // Update Scroll logic
- mScrollArea->logic();
+ if (mDialogState == DialogState::DONE)
+ return;
- // Synchronize label caption when necessary
- {
- MutexLocker lock(&mDownloadMutex);
+ const auto state = mDownload->getState();
+ float progress = 0.0f;
- if (mLabel->getCaption() != mNewLabelCaption)
- {
- mLabel->setCaption(mNewLabelCaption);
- mLabel->adjustSize();
- }
+ switch (state.status) {
+ case DownloadStatus::IN_PROGRESS: {
+ setLabel(mCurrentFile + " (" + toString((int) (state.progress * 100)) + "%)");
+ progress = state.progress;
+ break;
+ }
+
+ case DownloadStatus::CANCELED:
+ mDialogState = DialogState::DONE;
+
+ enablePlay();
+ setLabel(_("Download canceled"));
+ break;
+
+ case DownloadStatus::ERROR: {
+ mDialogState = DialogState::DONE;
- mProgressBar->setProgress(mDownloadProgress);
+ std::string error = "##1";
+ error += mDownload->getError();
+ error += "\n\n##1";
+ error += _("The update process is incomplete. "
+ "It is strongly recommended that you try again later.");
+ mBrowserBox->addRows(error);
+
+ int maxScroll = mScrollArea->getVerticalMaxScroll();
+ mScrollArea->setVerticalScrollAmount(maxScroll);
+
+ enablePlay();
+ setLabel(_("Error while downloading"));
+ break;
+ }
+
+ case DownloadStatus::COMPLETE:
+ downloadCompleted();
+ break;
}
- switch (mDownloadStatus)
+ mProgressBar->setProgress(progress);
+}
+
+void UpdaterWindow::downloadCompleted()
+{
+ switch (mDialogState)
{
- case UPDATE_ERROR: {
- std::string error = "##1";
- error += mDownload->getError();
- error += "\n\n";
- error += _("The update process is incomplete. "
- "It is strongly recommended that you try again later.");
- mBrowserBox->addRows(error);
-
- mScrollArea->setVerticalScrollAmount(
- mScrollArea->getVerticalMaxScroll());
- mDownloadStatus = UPDATE_COMPLETE;
- break;
- }
- case UPDATE_NEWS:
- if (mDownloadComplete)
- {
- // Parse current memory buffer as news and dispose of the data
- loadNews();
+ case DialogState::DOWNLOAD_NEWS:
+ loadNews();
- mCurrentFile = xmlUpdateFile;
- mStoreInMemory = false;
- mDownloadStatus = UPDATE_LIST;
- download(); // download() changes mDownloadComplete to false
- }
- break;
- case UPDATE_LIST:
- if (mDownloadComplete)
+ mDialogState = DialogState::DOWNLOAD_LIST;
+ startDownload(xmlUpdateFile, false);
+ break;
+
+ case DialogState::DOWNLOAD_LIST:
+ if (mCurrentFile == xmlUpdateFile)
+ {
+ mUpdateFiles = loadXMLFile(mUpdatesDir + "/" + xmlUpdateFile);
+ if (mUpdateFiles.empty())
{
- if (mCurrentFile == xmlUpdateFile)
- {
- mUpdateFiles = loadXMLFile(mUpdatesDir + "/" + xmlUpdateFile);
- if (mUpdateFiles.empty())
- {
- logger->log("Warning this server does not have a %s"
- " file falling back to %s",
- xmlUpdateFile.c_str(), txtUpdateFile.c_str());
-
- // If the resources.xml file fails, fall back onto a older version
- mCurrentFile = txtUpdateFile;
- mStoreInMemory = false;
- mDownloadStatus = UPDATE_LIST;
- download();
- break;
- }
- }
- else if (mCurrentFile == txtUpdateFile)
- {
- mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile);
- }
- mStoreInMemory = false;
- mDownloadStatus = UPDATE_RESOURCES;
+ logger->log("Warning this server does not have a %s"
+ " file falling back to %s",
+ xmlUpdateFile, txtUpdateFile);
+
+ // If the resources.xml file fails, fall back onto a older version
+ mDialogState = DialogState::DOWNLOAD_LIST;
+ startDownload(txtUpdateFile, false);
+ break;
}
- break;
- case UPDATE_RESOURCES:
- if (mDownloadComplete)
+ }
+ else if (mCurrentFile == txtUpdateFile)
+ {
+ mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile);
+ }
+
+ mDialogState = DialogState::DOWNLOAD_RESOURCES;
+ break;
+
+ case DialogState::DOWNLOAD_RESOURCES:
+ if (mUpdateIndex < mUpdateFiles.size())
+ {
+ const UpdateFile &thisFile = mUpdateFiles[mUpdateIndex];
+ if (!thisFile.required)
{
- if (mUpdateIndex < mUpdateFiles.size())
+ if (!(thisFile.type == "music" && config.downloadMusic))
{
- const UpdateFile &thisFile = mUpdateFiles[mUpdateIndex];
- if (!thisFile.required)
- {
- if (!(thisFile.type == "music" && config.downloadMusic))
- {
- mUpdateIndex++;
- break;
- }
- }
- mCurrentFile = thisFile.name;
- std::stringstream ss(thisFile.hash);
- ss >> std::hex >> mCurrentChecksum;
-
- std::string filename = mUpdatesDir + "/" + mCurrentFile;
- FILE *file = fopen(filename.c_str(), "r+b");
-
- if (!file || Net::Download::fadler32(file) != mCurrentChecksum)
- {
- if (file)
- fclose(file);
- download();
- }
- else
- {
- fclose(file);
- logger->log("%s already here", mCurrentFile.c_str());
- }
mUpdateIndex++;
- }
- else
- {
- // Download of updates completed
- mDownloadStatus = UPDATE_COMPLETE;
+ break;
}
}
- break;
- case UPDATE_COMPLETE:
- enable();
+
+ unsigned long checksum;
+ std::stringstream ss(thisFile.hash);
+ ss >> std::hex >> checksum;
+
+ std::string filename = mUpdatesDir + "/" + thisFile.name;
+ FILE *file = fopen(filename.c_str(), "r+b");
+
+ if (!file || Net::Download::fadler32(file) != checksum)
+ {
+ if (file)
+ fclose(file);
+ startDownload(thisFile.name, false, checksum);
+ }
+ else
+ {
+ fclose(file);
+ logger->log("%s already here", thisFile.name.c_str());
+ }
+ mUpdateIndex++;
+ }
+ else
+ {
+ // Download of updates completed
+ mDialogState = DialogState::DONE;
+ enablePlay();
setLabel(_("Completed"));
- mDownloadStatus = UPDATE_IDLE;
- break;
- case UPDATE_IDLE:
- break;
+ }
+ break;
+
+ case DialogState::DONE:
+ break;
}
}
diff --git a/src/gui/updaterwindow.h b/src/gui/updaterwindow.h
index 5c749e18..249da067 100644
--- a/src/gui/updaterwindow.h
+++ b/src/gui/updaterwindow.h
@@ -25,8 +25,6 @@
#include "net/download.h"
-#include "utils/mutex.h"
-
#include <guichan/actionlistener.hpp>
#include <guichan/keylistener.hpp>
@@ -73,27 +71,6 @@ class UpdaterWindow : public Window, public gcn::ActionListener,
~UpdaterWindow() override;
- /**
- * Set's progress bar status
- */
- void setProgress(float progress);
-
- /**
- * Set's label above progress
- */
- void setLabel(const std::string &);
-
- /**
- * Enables play button
- */
- void enable();
-
- /**
- * Loads and display news. Assumes the news file contents have been loaded
- * into the memory buffer.
- */
- void loadNews();
-
void action(const gcn::ActionEvent &event) override;
void keyPressed(gcn::KeyEvent &keyEvent) override;
@@ -101,38 +78,38 @@ class UpdaterWindow : public Window, public gcn::ActionListener,
void logic() override;
private:
- void download();
+ bool cancel();
+ void play();
- /**
- * Loads the updates this window has gotten into the resource manager
- */
- void loadUpdates();
+ void setLabel(const std::string &);
+ void enablePlay();
+ void startDownload(const std::string &fileName,
+ bool storeInMemory,
+ std::optional<unsigned long> adler32 = {});
+ void downloadCompleted();
/**
- * A download callback for progress updates.
+ * Loads and display news. Assumes the news file contents have been loaded
+ * into the memory buffer.
*/
- static int updateProgress(void *ptr, DownloadStatus status,
- size_t dltotal, size_t dlnow);
+ void loadNews();
/**
- * A libcurl callback for writing to memory.
+ * Loads the updates this window has gotten into the resource manager
*/
- static size_t memoryWrite(char *ptr, size_t size, size_t nmemb,
- void *stream);
+ void loadUpdates();
- enum UpdateDownloadStatus
+ enum class DialogState
{
- UPDATE_ERROR,
- UPDATE_IDLE,
- UPDATE_LIST,
- UPDATE_COMPLETE,
- UPDATE_NEWS,
- UPDATE_RESOURCES
+ DOWNLOAD_NEWS,
+ DOWNLOAD_LIST,
+ DOWNLOAD_RESOURCES,
+ DONE,
};
/** Status of the current download. */
- UpdateDownloadStatus mDownloadStatus = UPDATE_NEWS;
+ DialogState mDialogState = DialogState::DOWNLOAD_NEWS;
/** Host where we get the updated files. */
std::string mUpdateHost;
@@ -143,33 +120,6 @@ private:
/** The file currently downloading. */
std::string mCurrentFile;
- /** The new label caption to be set in the logic method. */
- std::string mNewLabelCaption;
-
- /** The new progress value to be set in the logic method. */
- float mDownloadProgress = 0.0f;
-
- /** The mutex used to guard access to mNewLabelCaption and mDownloadProgress. */
- Mutex mDownloadMutex;
-
- /** The Adler32 checksum of the file currently downloading. */
- unsigned long mCurrentChecksum = 0;
-
- /** A flag to indicate whether to use a memory buffer or a regular file. */
- bool mStoreInMemory = true;
-
- /** Flag that show if current download is complete. */
- bool mDownloadComplete = true;
-
- /** Flag that show if the user has canceled the update. */
- bool mUserCancel = false;
-
- /** Byte count currently downloaded in mMemoryBuffer. */
- size_t mDownloadedBytes = 0;
-
- /** Buffer for files downloaded to memory. */
- char *mMemoryBuffer = nullptr;
-
/** Download handle. */
std::unique_ptr<Net::Download> mDownload;
diff --git a/src/net/download.cpp b/src/net/download.cpp
index 2cfdc3a1..92656ac8 100644
--- a/src/net/download.cpp
+++ b/src/net/download.cpp
@@ -59,29 +59,25 @@ unsigned long Download::fadler32(FILE *file)
return adler;
}
-Download::Download(void *ptr,
- const std::string &url,
- DownloadUpdate updateFunction)
- : mPtr(ptr)
- , mUrl(url)
- , mUpdateFunction(updateFunction)
+Download::Download(const std::string &url)
+ : mUrl(url)
{
- mError = (char*) malloc(CURL_ERROR_SIZE);
mError[0] = 0;
-
- mOptions.cancel = false;
}
Download::~Download()
{
+ mCancel = true;
SDL_WaitThread(mThread, nullptr);
curl_slist_free_all(mHeaders);
- free(mError);
+ free(mBuffer);
}
void Download::addHeader(const char *header)
{
+ assert(!mThread); // Cannot add headers after starting download
+
mHeaders = curl_slist_append(mHeaders, header);
}
@@ -94,19 +90,24 @@ void Download::noCache()
void Download::setFile(const std::string &filename,
std::optional<unsigned long> adler32)
{
- mOptions.memoryWrite = false;
+ assert(!mThread); // Cannot set file after starting download
+
+ mMemoryWrite = false;
mFileName = filename;
mAdler = adler32;
}
-void Download::setWriteFunction(curl_write_callback write)
+void Download::setUseBuffer()
{
- mOptions.memoryWrite = true;
- mWriteFunction = write;
+ assert(!mThread); // Cannot set write function after starting download
+
+ mMemoryWrite = true;
}
bool Download::start()
{
+ assert(!mThread); // Download already started
+
logger->log("Starting download: %s", mUrl.c_str());
mThread = SDL_CreateThread(downloadThread, "Download", this);
@@ -115,8 +116,7 @@ bool Download::start()
{
logger->log("%s", DOWNLOAD_ERROR_MESSAGE_THREAD);
strncpy(mError, DOWNLOAD_ERROR_MESSAGE_THREAD, CURL_ERROR_SIZE - 1);
- mUpdateFunction(mPtr, DOWNLOAD_STATUS_THREAD_ERROR, 0, 0);
-
+ mState.lock()->status = DownloadStatus::ERROR;
return false;
}
@@ -126,23 +126,49 @@ bool Download::start()
void Download::cancel()
{
logger->log("Canceling download: %s", mUrl.c_str());
- mOptions.cancel = true;
+ mCancel = true;
}
-const char *Download::getError() const
+std::string_view Download::getBuffer() const
{
- return mError;
+ assert(mMemoryWrite); // Buffer not used
+ return std::string_view(mBuffer, mDownloadedBytes);
}
+/**
+ * A libcurl callback for reporting progress.
+ */
int Download::downloadProgress(void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
auto *d = reinterpret_cast<Download*>(clientp);
- DownloadStatus status = d->mOptions.cancel ? DOWNLOAD_STATUS_CANCELLED
- : DOWNLOAD_STATUS_IN_PROGRESS;
- return d->mUpdateFunction(d->mPtr, status, (size_t) dltotal, (size_t) dlnow);
+ auto state = d->mState.lock();
+ state->status = DownloadStatus::IN_PROGRESS;
+ state->progress = 0.0f;
+ if (dltotal > 0)
+ state->progress = static_cast<float>(dlnow) / dltotal;
+
+ return d->mCancel;
+}
+
+/**
+ * A libcurl callback for writing to memory.
+ */
+size_t Download::writeBuffer(char *ptr, size_t size, size_t nmemb, void *stream)
+{
+ auto *d = reinterpret_cast<Download *>(stream);
+
+ const size_t totalMem = size * nmemb;
+ d->mBuffer = (char *) realloc(d->mBuffer, d->mDownloadedBytes + totalMem);
+ if (d->mBuffer)
+ {
+ memcpy(d->mBuffer + d->mDownloadedBytes, ptr, totalMem);
+ d->mDownloadedBytes += totalMem;
+ }
+
+ return totalMem;
}
int Download::downloadThread(void *ptr)
@@ -151,13 +177,11 @@ int Download::downloadThread(void *ptr)
bool complete = false;
std::string outFilename;
- if (!d->mOptions.memoryWrite)
+ if (!d->mMemoryWrite)
outFilename = d->mFileName + ".part";
- for (int attempts = 0; attempts < 3 && !complete && !d->mOptions.cancel; ++attempts)
+ for (int attempts = 0; attempts < 3 && !complete && !d->mCancel; ++attempts)
{
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_STARTING, 0, 0);
-
CURL *curl = curl_easy_init();
if (!curl)
break;
@@ -169,11 +193,11 @@ int Download::downloadThread(void *ptr)
FILE *file = nullptr;
- if (d->mOptions.memoryWrite)
+ if (d->mMemoryWrite)
{
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, d->mWriteFunction);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, d->mPtr);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Download::writeBuffer);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
}
else
{
@@ -189,7 +213,7 @@ int Download::downloadThread(void *ptr)
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, d->mError);
curl_easy_setopt(curl, CURLOPT_URL, d->mUrl.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
- curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, downloadProgress);
+ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, &Download::downloadProgress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, ptr);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
@@ -199,7 +223,7 @@ int Download::downloadThread(void *ptr)
if (res == CURLE_ABORTED_BY_CALLBACK)
{
- d->mOptions.cancel = true;
+ d->mCancel = true;
if (file)
{
@@ -224,9 +248,9 @@ int Download::downloadThread(void *ptr)
break;
}
- if (!d->mOptions.memoryWrite)
+ if (!d->mMemoryWrite)
{
- // Don't check resources.xml checksum
+ // Check the checksum if available
if (d->mAdler)
{
unsigned long adler = fadler32(file);
@@ -274,13 +298,13 @@ int Download::downloadThread(void *ptr)
fclose(file);
}
- if (!d->mOptions.cancel)
- {
- if (complete)
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_COMPLETE, 0, 0);
- else
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
- }
+ auto state = d->mState.lock();
+ if (d->mCancel)
+ state->status = DownloadStatus::CANCELED;
+ else if (complete)
+ state->status = DownloadStatus::COMPLETE;
+ else
+ state->status = DownloadStatus::ERROR;
return 0;
}
diff --git a/src/net/download.h b/src/net/download.h
index be2e374f..20a7fc74 100644
--- a/src/net/download.h
+++ b/src/net/download.h
@@ -18,22 +18,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "utils/mutex.h"
+
#include <cstdio>
-#include <string>
#include <optional>
+#include <string>
#include <curl/curl.h>
#pragma once
-enum DownloadStatus
+enum class DownloadStatus
{
- DOWNLOAD_STATUS_CANCELLED = -3,
- DOWNLOAD_STATUS_THREAD_ERROR = -2,
- DOWNLOAD_STATUS_ERROR = -1,
- DOWNLOAD_STATUS_STARTING = 0,
- DOWNLOAD_STATUS_IN_PROGRESS,
- DOWNLOAD_STATUS_COMPLETE
+ IN_PROGRESS,
+ CANCELED,
+ ERROR,
+ COMPLETE
};
struct SDL_Thread;
@@ -43,17 +43,13 @@ namespace Net {
class Download
{
public:
- /**
- * Callback function for download updates.
- *
- * @param ptr Pointer passed to Download constructor
- * @param status Current download status
- * @param dltotal Total number of bytes to download
- * @param dlnow Number of bytes downloaded so far
- */
- using DownloadUpdate = int (*)(void *, DownloadStatus, size_t, size_t);
+ struct State
+ {
+ DownloadStatus status = DownloadStatus::IN_PROGRESS;
+ float progress = 0.0f;
+ };
- Download(void *ptr, const std::string &url, DownloadUpdate updateFunction);
+ Download(const std::string &url);
~Download();
void addHeader(const char *header);
@@ -66,44 +62,66 @@ class Download
void setFile(const std::string &filename,
std::optional<unsigned long> adler32 = {});
- void setWriteFunction(curl_write_callback write);
+ void setUseBuffer();
/**
* Starts the download thread.
- * @returns true if thread was created
- * false if the thread could not be made or download wasn't
- * properly setup
+ * @returns whether the thread could be created
*/
bool start();
/**
- * Cancels the download. Returns immediately, the cancelled status will
+ * Cancels the download. Returns immediately, the canceled status will
* be noted in the next available update call.
*/
void cancel();
+ /**
+ * Returns a view on the downloaded data.
+ */
+ std::string_view getBuffer() const;
+
+ State getState();
+
const char *getError() const;
static unsigned long fadler32(FILE *file);
private:
- static int downloadThread(void *ptr);
static int downloadProgress(void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);
- void *mPtr;
+
+ static size_t writeBuffer(char *ptr, size_t size, size_t nmemb,
+ void *stream);
+
+ static int downloadThread(void *ptr);
+
+ ThreadSafe<State> mState;
std::string mUrl;
- struct {
- unsigned cancel : 1;
- unsigned memoryWrite: 1;
- } mOptions;
+ bool mCancel = false;
+ bool mMemoryWrite = false;
std::string mFileName;
- curl_write_callback mWriteFunction = nullptr;
std::optional<unsigned long> mAdler;
- DownloadUpdate mUpdateFunction;
SDL_Thread *mThread = nullptr;
curl_slist *mHeaders = nullptr;
- char *mError;
+ char mError[CURL_ERROR_SIZE];
+
+ /** Byte count currently downloaded in mMemoryBuffer. */
+ size_t mDownloadedBytes = 0;
+
+ /** Buffer for files downloaded to memory. */
+ char *mBuffer = nullptr;
};
+inline Download::State Download::getState()
+{
+ return *mState.lock();
+}
+
+inline const char *Download::getError() const
+{
+ return mError;
+}
+
} // namespace Net
diff --git a/src/net/tmwa/tradehandler.cpp b/src/net/tmwa/tradehandler.cpp
index 60732eef..c129cfd4 100644
--- a/src/net/tmwa/tradehandler.cpp
+++ b/src/net/tmwa/tradehandler.cpp
@@ -131,7 +131,7 @@ void TradeHandler::handleMessage(MessageIn &msg)
"doesn't exist."));
break;
case 2: // Invite request check failed...
- serverNotice(_("Trade cancelled due to an unknown "
+ serverNotice(_("Trade canceled due to an unknown "
"reason."));
break;
case 3: // Trade accepted
@@ -140,11 +140,11 @@ void TradeHandler::handleMessage(MessageIn &msg)
tradePartnerName.c_str()));
tradeWindow->setVisible(true);
break;
- case 4: // Trade cancelled
+ case 4: // Trade canceled
if (player_relations.hasPermission(tradePartnerName,
PlayerPermissions::SPEECH_LOG))
{
- serverNotice(strprintf(_("Trade with %s cancelled."),
+ serverNotice(strprintf(_("Trade with %s canceled."),
tradePartnerName.c_str()));
}
// otherwise ignore silently
diff --git a/src/utils/mutex.h b/src/utils/mutex.h
index 3e01eb76..a0c72e95 100644
--- a/src/utils/mutex.h
+++ b/src/utils/mutex.h
@@ -102,3 +102,42 @@ inline MutexLocker::~MutexLocker()
if (mMutex)
mMutex->unlock();
}
+
+/**
+ * A template class for wrapping data that is accessed by multiple threads.
+ */
+template <typename T>
+class ThreadSafe
+{
+ class Locked : private MutexLocker
+ {
+ public:
+ Locked(T &data, Mutex &mutex)
+ : MutexLocker(&mutex)
+ , mData(data)
+ {}
+
+ Locked(Locked&& rhs) = delete;
+ Locked(const Locked&) = delete;
+ Locked& operator=(const Locked&) = delete;
+ Locked& operator=(Locked&&) = delete;
+
+ T &operator*() const { return mData; }
+ T *operator->() const { return &mData; }
+
+ private:
+ T &mData;
+ };
+
+public:
+ ThreadSafe() = default;
+ ThreadSafe(const T &data)
+ : mData(data)
+ {}
+
+ Locked lock() { return { mData, mMutex }; }
+
+private:
+ T mData;
+ Mutex mMutex;
+};