[springlobby] 01/07: New upstream version 0.256+dfsg
Markus Koschany
apo at moszumanska.debian.org
Sat May 6 18:46:08 UTC 2017
This is an automated email from the git hooks/post-receive script.
apo pushed a commit to branch master
in repository springlobby.
commit c31d65a27f39cfa60a7c12ae82c357aca9df6f68
Author: Markus Koschany <apo at debian.org>
Date: Sat May 6 20:22:21 2017 +0200
New upstream version 0.256+dfsg
---
.travis.yml | 1 +
ChangeLog | 6 +
VERSION | 2 +-
springlobby_config.h | 2 +-
src/CMakeLists.txt | 1 +
src/battle.cpp | 19 +-
src/downloader/lib/.travis.yml | 3 +-
src/downloader/lib/CMakeLists.txt | 2 +-
src/downloader/lib/src/CMakeLists.txt | 6 +-
.../lib/src/Downloader/Http/HttpDownloader.cpp | 9 +
.../lib/src/Downloader/Http/HttpDownloader.h | 2 +-
src/downloader/lib/src/Downloader/IDownloader.cpp | 10 +
src/downloader/lib/src/Downloader/IDownloader.h | 13 +-
.../lib/src/Downloader/Rapid/RapidDownloader.cpp | 3 +-
.../lib/src/Downloader/Rapid/RapidDownloader.h | 2 +-
src/downloader/lib/src/Downloader/Rapid/Repo.cpp | 2 +-
src/downloader/lib/src/Downloader/Rapid/Sdp.cpp | 226 ++++++++++-----------
src/downloader/lib/src/Downloader/Rapid/Sdp.h | 22 +-
src/downloader/lib/src/FileSystem/File.cpp | 6 +-
src/downloader/lib/src/FileSystem/FileSystem.cpp | 95 +++++++--
src/downloader/lib/src/FileSystem/FileSystem.h | 9 +-
src/downloader/lib/src/FileSystem/HashMD5.h | 2 +-
src/downloader/lib/src/lib/CMakeLists.txt | 10 +-
.../lib/src/lsl/lslunitsync/CMakeLists.txt | 2 +-
src/downloader/lib/src/lsl/lslunitsync/c_api.cpp | 6 +
src/downloader/lib/src/lsl/lslunitsync/c_api.h | 1 +
.../lib/src/lsl/lslunitsync/unitsync.cpp | 89 ++++----
src/downloader/lib/src/lsl/lslunitsync/unitsync.h | 6 +-
src/downloader/lib/src/main.cpp | 6 +
src/downloader/lib/src/pr-downloader.cpp | 13 +-
src/downloader/lib/src/pr-downloader.h | 12 ++
src/downloader/lib/test/CMakeLists.txt | 11 +-
src/gui/chatpanel.cpp | 2 +-
src/gui/singleplayertab.cpp | 14 +-
src/gui/ui.cpp | 14 --
src/gui/ui.h | 1 -
src/ibattle.cpp | 48 +++--
src/serverevents.cpp | 2 +-
src/socket.cpp | 2 +-
src/springlobbyapp.cpp | 7 +
src/stacktrace.h | 2 +
src/sysinfo.cpp | 5 +
src/tasserver.cpp | 25 ++-
43 files changed, 451 insertions(+), 270 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 1a3d176..38c1300 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,7 @@ compiler:
- clang
sudo: required
dist: trusty
+cache: ccache
install:
- sudo apt-get install -y libwxgtk3.0-dev libboost-thread1.55-dev libboost-system1.55-dev
diff --git a/ChangeLog b/ChangeLog
index 3148a6c..1e6e152 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
ChangeLog of Springlobby
+## 0.256
+ - lazy init of unitsync (faster startup)
+ - fix crash when Scroll wheel over games in single player tab
+ - always validate rapid downloads
+ - don't log private conversations to springlobby.log
+
## 0.255
- fix downloading with special chars in springlobby's data dirs
- merge lsl into prdownloader submodule
diff --git a/VERSION b/VERSION
index e6ab191..020b4db 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.255
+0.256
diff --git a/springlobby_config.h b/springlobby_config.h
index 136af5a..85b59c4 100644
--- a/springlobby_config.h
+++ b/springlobby_config.h
@@ -6,6 +6,6 @@
#undef VERSION
/* the git tag / commit we build from */
-#define VERSION "0.255"
+#define VERSION "0.256"
#endif /* SPRINGLOBBY_HEADERGUARD_CONFIG_H */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c8933ed..ae57c27 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -376,6 +376,7 @@ target_include_directories(springlobby
PRIVATE ${CURL_INCLUDE_DIR}
PRIVATE ${Boost_INCLUDE_DIRS}
PRIVATE ${springlobby_SOURCE_DIR}/src
+ PRIVATE ${springlobby_SOURCE_DIR}/src/downloader/lib/src
PRIVATE ${springlobby_SOURCE_DIR}/src/downloader/lib/src/lsl
)
diff --git a/src/battle.cpp b/src/battle.cpp
index f1860c4..e758c06 100644
--- a/src/battle.cpp
+++ b/src/battle.cpp
@@ -801,22 +801,6 @@ void shuffle(std::vector<User*>& players) // proper shuffle.
}
}
-/*
-bool ClanRemovalFunction(const std::map<wxString, Alliance>::value_type &v){
- return v.second.players.size()<2;
-}
-*/
-/*
-struct ClannersRemovalPredicate{
- std::map<wxString, Alliance> &clans;
- PlayerRemovalPredicate(std::map<wxString, Alliance> &clans_):clans(clans_)
- {
- }
- bool operator()(User *u) const{
- return clans.find(u->GetClan());
- }
-}*/
-
void Battle::Autobalance(BalanceType balance_type, bool support_clans, bool strong_clans, int numallyteams)
{
wxLogMessage(_T("Autobalancing alliances, type=%d, clans=%d, strong_clans=%d, numallyteams=%d"), balance_type, support_clans, strong_clans, numallyteams);
@@ -1108,8 +1092,9 @@ void Battle::FixTeamIDs(BalanceType balance_type, bool support_clans, bool stron
void Battle::OnUnitsyncReloaded(wxEvent& /*data*/)
{
- if (m_is_self_in)
+ if (m_is_self_in) {
SendMyBattleStatus();
+ }
}
void Battle::ShouldAutoUnspec()
diff --git a/src/downloader/lib/.travis.yml b/src/downloader/lib/.travis.yml
index 919fa00..4addaa8 100644
--- a/src/downloader/lib/.travis.yml
+++ b/src/downloader/lib/.travis.yml
@@ -5,6 +5,7 @@ compiler:
sudo: required
dist: trusty
+cache: ccache
install:
- sudo apt-get install -y gcc-4.7 g++-4.7 cmake libcurl4-openssl-dev libjsoncpp-dev
@@ -16,5 +17,5 @@ before_script:
script:
- make -j2
- sudo make install
- - src/pr-downloader ba:stable
+ - src/pr-downloader devgame:test
- src/pr-downloader --rapid-validate
diff --git a/src/downloader/lib/CMakeLists.txt b/src/downloader/lib/CMakeLists.txt
index 25b4e6e..112efc5 100644
--- a/src/downloader/lib/CMakeLists.txt
+++ b/src/downloader/lib/CMakeLists.txt
@@ -132,7 +132,7 @@ if(NOT MINIZIP_FOUND)
endif()
-subdirs(src)
+add_subdirectory(src)
find_program(CLANG_FORMAT_BINARY NAMES clang-format-3.7 clang-format-3.6 clang-format-3.5 clang-format-3.4 clang-format)
diff --git a/src/downloader/lib/src/CMakeLists.txt b/src/downloader/lib/src/CMakeLists.txt
index f1681bc..29df0a7 100644
--- a/src/downloader/lib/src/CMakeLists.txt
+++ b/src/downloader/lib/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-subdirs(lib)
+add_subdirectory(lib)
if (PRD_ARCHIVE_SUPPORT)
set(archivessrc FileSystem/SevenZipArchive.cpp FileSystem/ZipArchive.cpp)
@@ -100,6 +100,7 @@ if (PRD_SHAREDLIB)
LIBRARY DESTINATION ${PRD_LIBDIR}
ARCHIVE DESTINATION ${PRD_LIBDIR} )
endif()
+ target_include_directories(${PRDOWNLOADER_SHARED} PRIVATE ${pr-downloader_SOURCE_DIR}/src)
target_link_libraries( ${PRDOWNLOADER_SHARED} ${PRDOWNLOADER_LIBS} )
endif()
@@ -115,6 +116,7 @@ if (PRD_STATICLIB)
LIBRARY DESTINATION ${PRD_LIBDIR}
ARCHIVE DESTINATION ${PRD_LIBDIR} )
endif()
+ target_include_directories(${PRDOWNLOADER_STATIC} PRIVATE ${pr-downloader_SOURCE_DIR}/src)
target_link_libraries( ${PRDOWNLOADER_STATIC} ${PRDOWNLOADER_LIBS} ${CMAKE_DL_LIBS})
endif()
@@ -171,5 +173,5 @@ endif()
OPTION(PRD_ENABLE_LSL "compile lsl modules" OFF)
if (PRD_ENABLE_LSL)
- subdirs(lsl)
+ add_subdirectory(lsl)
endif()
diff --git a/src/downloader/lib/src/Downloader/Http/HttpDownloader.cpp b/src/downloader/lib/src/Downloader/Http/HttpDownloader.cpp
index 90fa3d2..19ccc57 100644
--- a/src/downloader/lib/src/Downloader/Http/HttpDownloader.cpp
+++ b/src/downloader/lib/src/Downloader/Http/HttpDownloader.cpp
@@ -37,6 +37,9 @@ CHttpDownloader::~CHttpDownloader()
static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb,
void* userp)
{
+ if (IDownloader::AbortDownloads())
+ return -1;
+
const size_t realsize = size * nmemb;
std::string* res = static_cast<std::string*>(userp);
res->append((char*)contents, realsize);
@@ -46,6 +49,9 @@ static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb,
static int progress_func(DownloadData* data, double total, double done, double,
double)
{
+ if (IDownloader::AbortDownloads())
+ return -1;
+
data->download->progress = done;
if (IDownloader::listener != NULL) {
IDownloader::listener(done, total);
@@ -210,6 +216,9 @@ bool CHttpDownloader::search(std::list<IDownload*>& res,
static size_t multi_write_data(void* ptr, size_t size, size_t nmemb,
DownloadData* data)
{
+ if (IDownloader::AbortDownloads())
+ return -1;
+
// LOG_DEBUG("%d %d",size, nmemb);
if (!data->got_ranges) {
LOG_INFO("Server refused ranges"); // The server refused ranges , download
diff --git a/src/downloader/lib/src/Downloader/Http/HttpDownloader.h b/src/downloader/lib/src/Downloader/Http/HttpDownloader.h
index 9fd436b..cd66743 100644
--- a/src/downloader/lib/src/Downloader/Http/HttpDownloader.h
+++ b/src/downloader/lib/src/Downloader/Http/HttpDownloader.h
@@ -3,7 +3,7 @@
#ifndef HTTP_DOWNLOAD_H
#define HTTP_DOWNLOAD_H
-#include "../IDownloader.h"
+#include "Downloader/IDownloader.h"
#include <curl/curl.h>
#include <string>
diff --git a/src/downloader/lib/src/Downloader/IDownloader.cpp b/src/downloader/lib/src/Downloader/IDownloader.cpp
index 8109064..3e657fb 100644
--- a/src/downloader/lib/src/Downloader/IDownloader.cpp
+++ b/src/downloader/lib/src/Downloader/IDownloader.cpp
@@ -28,6 +28,16 @@ void IDownloader::Shutdown()
rapiddl = NULL;
curl_global_cleanup();
}
+static bool abortDownloads = false;
+void IDownloader::SetAbortDownloads(bool value)
+{
+ abortDownloads = value;
+}
+
+bool IDownloader::AbortDownloads()
+{
+ return abortDownloads;
+}
IDownloader* IDownloader::GetHttpInstance()
{
diff --git a/src/downloader/lib/src/Downloader/IDownloader.h b/src/downloader/lib/src/Downloader/IDownloader.h
index 6785425..970a69e 100644
--- a/src/downloader/lib/src/Downloader/IDownloader.h
+++ b/src/downloader/lib/src/Downloader/IDownloader.h
@@ -18,17 +18,22 @@ public:
static IDownloader* GetRapidInstance();
/**
- Initialize all Downloaders
- */
+ *Initialize all Downloaders
+ */
static void Initialize();
/**
- Shutdown all Downloaders
- */
+ * Shutdown all Downloaders
+ */
static void Shutdown();
virtual ~IDownloader()
{
}
+ /**
+ * Aborts all ongoing downloads
+ */
+ static void SetAbortDownloads(bool value);
+ static bool AbortDownloads();
/**
download specificed download
diff --git a/src/downloader/lib/src/Downloader/Rapid/RapidDownloader.cpp b/src/downloader/lib/src/Downloader/Rapid/RapidDownloader.cpp
index 533d5b3..62ac84b 100644
--- a/src/downloader/lib/src/Downloader/Rapid/RapidDownloader.cpp
+++ b/src/downloader/lib/src/Downloader/Rapid/RapidDownloader.cpp
@@ -32,7 +32,7 @@ CRapidDownloader::~CRapidDownloader()
sdps.clear();
}
-void CRapidDownloader::addRemoteDsp(CSdp& sdp)
+void CRapidDownloader::addRemoteSdp(CSdp& sdp)
{
sdps.push_back(sdp);
}
@@ -195,6 +195,7 @@ bool CRapidDownloader::parse()
}
gzFile fp = gzdopen(fileno(f), "rb");
if (fp == Z_NULL) {
+ fclose(f);
LOG_ERROR("Could not open %s", path.c_str());
return false;
}
diff --git a/src/downloader/lib/src/Downloader/Rapid/RapidDownloader.h b/src/downloader/lib/src/Downloader/Rapid/RapidDownloader.h
index 50eb507..f388a1d 100644
--- a/src/downloader/lib/src/Downloader/Rapid/RapidDownloader.h
+++ b/src/downloader/lib/src/Downloader/Rapid/RapidDownloader.h
@@ -36,7 +36,7 @@ public:
bool setOption(const std::string& key, const std::string& value) override;
- void addRemoteDsp(CSdp& dsp);
+ void addRemoteSdp(CSdp& dsp);
/**
parses a rep master-file
*/
diff --git a/src/downloader/lib/src/Downloader/Rapid/Repo.cpp b/src/downloader/lib/src/Downloader/Rapid/Repo.cpp
index c7558d3..e1142a2 100644
--- a/src/downloader/lib/src/Downloader/Rapid/Repo.cpp
+++ b/src/downloader/lib/src/Downloader/Rapid/Repo.cpp
@@ -80,7 +80,7 @@ bool CRepo::parse()
// create new repo from url
CSdp sdptmp = CSdp(items[0], items[1], items[3], items[2], repourl);
- rapid->addRemoteDsp(sdptmp);
+ rapid->addRemoteSdp(sdptmp);
}
int errnum = Z_OK;
const char* errstr = gzerror(fp, &errnum);
diff --git a/src/downloader/lib/src/Downloader/Rapid/Sdp.cpp b/src/downloader/lib/src/Downloader/Rapid/Sdp.cpp
index 8555ac4..7a8f05d 100644
--- a/src/downloader/lib/src/Downloader/Rapid/Sdp.cpp
+++ b/src/downloader/lib/src/Downloader/Rapid/Sdp.cpp
@@ -18,10 +18,9 @@
CSdp::CSdp(const std::string& shortname, const std::string& md5,
const std::string& name, const std::string& depends,
- const std::string& url)
+ const std::string& baseUrl)
: m_download(NULL)
, downloadInitialized(false)
- , globalFiles(NULL)
, file_handle(NULL)
, file_pos(0)
, skipped(false)
@@ -29,11 +28,18 @@ CSdp::CSdp(const std::string& shortname, const std::string& md5,
, name(name)
, md5(md5)
, shortname(shortname)
- , url(url)
+ , baseUrl(baseUrl)
, depends(depends)
, downloaded(false)
{
- memset(this->cursize_buf, 0, LENGTH_SIZE);
+ memset(cursize_buf, 0, LENGTH_SIZE);
+ const std::string dir =
+ fileSystem->getSpringDir() + PATH_DELIMITER + "packages" + PATH_DELIMITER;
+ if (!fileSystem->directoryExists(dir)) {
+ fileSystem->createSubdirs(dir);
+ }
+ sdpPath = dir + md5 + ".sdp";
+ LOG_DEBUG("%s", sdpPath.c_str());
}
CSdp::~CSdp()
@@ -59,54 +65,57 @@ bool createPoolDirs(const std::string& root)
return true;
}
-bool CSdp::download(IDownload* download)
+bool CSdp::downloadSelf(IDownload* dl)
{
- if (downloaded) // allow download only once of the same sdp
- return true;
- m_download = download;
- filename =
- fileSystem->getSpringDir() + PATH_DELIMITER + "packages" + PATH_DELIMITER;
- LOG_DEBUG("%s", filename.c_str());
- if (!fileSystem->directoryExists(filename)) {
- fileSystem->createSubdirs(filename);
+
+ const std::string tmpFile = sdpPath + ".tmp";
+ IDownload tmpdl(tmpFile);
+ tmpdl.addMirror(baseUrl + "/packages/" + md5 + ".sdp");
+ if(!httpDownload->download(&tmpdl)) {
+ LOG_ERROR("Couldn't download %s", (md5 + ".sdp").c_str());
+ return false;
}
- int count = 0;
- filename += this->md5 + ".sdp";
- const std::string tmpFile = filename + ".tmp";
- std::list<FileData*> files;
+
- bool rename = false;
- if (!fileSystem->fileExists(filename)) { //.sdp isn't avaiable, download it
- IDownload dl(tmpFile);
- dl.addMirror(url + "/packages/" + this->md5 + ".sdp");
- httpDownload->download(&dl);
- fileSystem->parseSdp(tmpFile, files); // parse downloaded file
- rename = true;
- } else {
- fileSystem->parseSdp(filename, files); // parse downloaded file
+ if (!fileSystem->Rename(tmpFile, sdpPath)) {
+ LOG_ERROR("Couldn't rename %s to %s", tmpFile.c_str(), sdpPath.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+bool CSdp::download(IDownload* dl)
+{
+ if (downloaded) // allow download only once of the same sdp
+ return true;
+ m_download = dl;
+ if ((!fileSystem->fileExists(sdpPath)) || (!fileSystem->parseSdp(sdpPath, files))) {// parse downloaded file
+ if (!downloadSelf(dl))
+ return false;
+ fileSystem->parseSdp(sdpPath, files);
}
- HashMD5 md5 = HashMD5();
- FileData tmp = FileData();
int i = 0;
- for (FileData* filedata : files) { // check which file are available on local
- // disk -> create list of files to download
+ int count = 0;
+ for (FileData& filedata: files) { // check which file are available on local
+ // disk -> create list of files to download
+ HashMD5 fileMd5;
i++;
- md5.Set(filedata->md5, sizeof(filedata->md5));
+ fileMd5.Set(filedata.md5, sizeof(filedata.md5));
std::string file;
- fileSystem->getPoolFilename(md5.toString(), file);
- if (!fileSystem->fileExists(
- file)) { // add non-existing files to download list
+ fileSystem->getPoolFilename(fileMd5.toString(), file);
+ if (!fileSystem->fileExists(file)) { // add non-existing files to download list
count++;
- filedata->download = true;
+ filedata.download = true;
} else {
- filedata->download = false;
+ filedata.download = false;
}
if (i % 30 == 0) {
LOG_DEBUG("\r%d/%d checked", i, (int)files.size());
}
}
- LOG_DEBUG("\r%d/%d need to download %d files", i, (unsigned int)files.size(),
+ LOG_DEBUG("\r%d/%d need to download %d files", i, (int)files.size(),
count);
std::string root = fileSystem->getSpringDir();
@@ -115,35 +124,23 @@ bool CSdp::download(IDownload* download)
root += PATH_DELIMITER;
if (!createPoolDirs(root)) {
LOG_ERROR("Creating pool directories failed");
- count = 0;
+ return false;
}
- if (count > 0) {
- downloaded =
- downloadStream(this->url + "/streamer.cgi?" + this->md5, files);
- if (!downloaded) {
- LOG_ERROR("Couldn't download files for %s", this->md5.c_str());
- fileSystem->removeFile(tmpFile);
- fileSystem->removeFile(filename);
- return false;
- }
- LOG_DEBUG("Sucessfully downloaded %d files: %s %s", count,
- shortname.c_str(), name.c_str());
- } else {
- LOG_DEBUG("Already downloaded: %s", shortname.c_str());
- downloaded = true;
+ if (!downloadStream()) {
+ LOG_ERROR("Couldn't download files for %s", md5.c_str());
+ fileSystem->removeFile(sdpPath);
+ return false;
}
+ LOG_DEBUG("Sucessfully downloaded %d files: %s %s", count,
+ shortname.c_str(), name.c_str());
- for (FileData* filedata : files) { // free memory
- delete filedata;
- }
- if ((rename) && (!fileSystem->Rename(tmpFile, filename))) {
- LOG_ERROR("Couldn't rename %s to %s", tmpFile.c_str(), filename.c_str());
+ if (!fileSystem->validateSDP(sdpPath)) { //FIXME: in this call only the downloaded files should be checked
+ LOG_ERROR("Validation failed");
return false;
}
- if (downloaded) {
- download->state = IDownload::STATE_FINISHED;
- }
- return downloaded;
+ downloaded = true;
+ dl->state = IDownload::STATE_FINISHED;
+ return true;
}
/**
@@ -155,10 +152,12 @@ bool CSdp::download(IDownload* download)
static size_t write_streamed_data(const void* tmp, size_t size, size_t nmemb,
CSdp* sdp)
{
+ if (IDownloader::AbortDownloads())
+ return -1;
char buf[CURL_MAX_WRITE_SIZE];
memcpy(&buf, tmp, CURL_MAX_WRITE_SIZE);
if (!sdp->downloadInitialized) {
- sdp->list_it = sdp->globalFiles->begin();
+ sdp->list_it = sdp->files.begin();
sdp->downloadInitialized = true;
sdp->file_handle = NULL;
sdp->file_name = "";
@@ -170,22 +169,26 @@ static size_t write_streamed_data(const void* tmp, size_t size, size_t nmemb,
while (buf_pos < buf_end) { // all bytes written?
if (sdp->file_handle == NULL) { // no open file, create one
- while ((!(*sdp->list_it)->download == true) &&
- (sdp->list_it != sdp->globalFiles->end())) { // get file
+ while (!sdp->list_it->download) { // get file
++sdp->list_it;
}
- HashMD5 md5;
- md5.Set((*sdp->list_it)->md5, sizeof((*sdp->list_it)->md5));
- fileSystem->getPoolFilename(md5.toString(), sdp->file_name);
+ assert(sdp->list_it != sdp->files.end());
+
+ FileData& fd = *(sdp->list_it);
+ HashMD5 fileMd5;
+
+ fileMd5.Set(fd.md5, sizeof(fd.md5));
+ fileSystem->getPoolFilename(fileMd5.toString(), sdp->file_name);
sdp->file_handle = new CFile();
if (sdp->file_handle == NULL) {
- LOG_ERROR("couldn't open %s", (*sdp->list_it)->name.c_str());
+ LOG_ERROR("couldn't open %s", fd.name.c_str());
return -1;
}
sdp->file_handle->Open(sdp->file_name);
sdp->file_pos = 0;
}
assert(sdp->file_handle != NULL);
+ FileData& fd = *(sdp->list_it);
if (sdp->skipped < LENGTH_SIZE) { // check if we skipped all 4 bytes for
// file length, if not so, skip them
const int toskip =
@@ -198,13 +201,13 @@ static size_t write_streamed_data(const void* tmp, size_t size, size_t nmemb,
sdp->skipped += toskip;
buf_pos += toskip;
if (sdp->skipped == LENGTH_SIZE) { // all length bytes read, parse
- (*sdp->list_it)->compsize = parse_int32(sdp->cursize_buf);
- assert((*sdp->list_it)->size + 2000 >= (*sdp->list_it)->compsize);
+ fd.compsize = parse_int32(sdp->cursize_buf);
+ assert(fd.size + 2000 >= fd.compsize);
}
}
if (sdp->skipped == LENGTH_SIZE) { // length bytes read
const int towrite =
- intmin((*sdp->list_it)->compsize -
+ intmin(fd.compsize -
sdp->file_pos, // minimum of bytes to write left in file
// and bytes to write left in buf
buf_end - buf_pos);
@@ -222,11 +225,11 @@ static size_t write_streamed_data(const void* tmp, size_t size, size_t nmemb,
sdp->file_pos += res;
if (sdp->file_pos >=
- (*sdp->list_it)->compsize) { // file finished -> next file
+ fd.compsize) { // file finished -> next file
sdp->file_handle->Close();
delete sdp->file_handle;
sdp->file_handle = NULL;
- if (!fileSystem->fileIsValid(*sdp->list_it, sdp->file_name.c_str())) {
+ if (!fileSystem->fileIsValid(&fd, sdp->file_name.c_str())) {
LOG_ERROR("File is broken?!: %s", sdp->file_name.c_str());
fileSystem->removeFile(sdp->file_name.c_str());
return -1;
@@ -243,29 +246,30 @@ static size_t write_streamed_data(const void* tmp, size_t size, size_t nmemb,
/** *
draw a nice download status-bar
*/
-static int progress_func(CSdp& csdp, double TotalToDownload,
+static int progress_func(CSdp& sdp, double TotalToDownload,
double NowDownloaded, double TotalToUpload,
double NowUploaded)
{
-
- (void)csdp;
+ if (IDownloader::AbortDownloads())
+ return -1;
+ (void)sdp;
(void)TotalToUpload;
(void)NowUploaded; // remove unused warning
- csdp.m_download->rapid_size[&csdp] = TotalToDownload;
- csdp.m_download->map_rapid_progress[&csdp] = NowDownloaded;
+ sdp.m_download->rapid_size[&sdp] = TotalToDownload;
+ sdp.m_download->map_rapid_progress[&sdp] = NowDownloaded;
uint64_t total = 0;
- for (auto it : csdp.m_download->rapid_size) {
+ for (auto it : sdp.m_download->rapid_size) {
total += it.second;
}
- csdp.m_download->size = total;
+ sdp.m_download->size = total;
if (IDownloader::listener != nullptr) {
IDownloader::listener(NowDownloaded, TotalToDownload);
}
total = 0;
- for (auto it : csdp.m_download->map_rapid_progress) {
+ for (auto it : sdp.m_download->map_rapid_progress) {
total += it.second;
}
- csdp.m_download->progress = total;
+ sdp.m_download->progress = total;
if (TotalToDownload == NowDownloaded) // force output when download is
// finished
LOG_PROGRESS(NowDownloaded, TotalToDownload, true);
@@ -274,56 +278,48 @@ static int progress_func(CSdp& csdp, double TotalToDownload,
return 0;
}
-bool CSdp::downloadStream(const std::string& url, std::list<FileData*> files)
+bool CSdp::downloadStream()
{
- CurlWrapper* curlw = new CurlWrapper();
- if (!curlw) {
- return false;
- }
+ std::string downloadUrl = baseUrl + "/streamer.cgi?" + md5;
+ CurlWrapper curlw;
+
CURLcode res;
LOG_INFO("Using rapid");
- LOG_INFO(url.c_str());
+ LOG_INFO(downloadUrl.c_str());
- curl_easy_setopt(curlw->GetHandle(), CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curlw.GetHandle(), CURLOPT_URL, downloadUrl.c_str());
+
+ const int buflen = (files.size() + 7) / 8;
+ std::vector<char> buf(buflen, 0);
- int buflen = files.size() / 8;
- if (files.size() % 8 != 0)
- buflen++;
- char* buf =
- (char*)malloc(buflen); // FIXME: compress blockwise and not all at once
- memset(buf, 0, buflen);
- int destlen = files.size() * 2;
- LOG_DEBUG("%d %d %d", (int)files.size(), buflen, destlen);
int i = 0;
- for (FileData* it : files) {
- if (it->download == true) {
+ for (FileData& fd: files) {
+ if (fd.download) {
buf[i / 8] |= (1 << (i % 8));
}
i++;
}
- char* dest = (char*)malloc(destlen);
- gzip_str(buf, buflen, dest, &destlen);
+ int destlen = files.size() * 2;
+ std::vector<char> dest(destlen, 0);
+ LOG_DEBUG("%d %d %d", (int)files.size(), buflen, destlen);
+
+ gzip_str(&buf[0], buflen, &dest[0], &destlen);
- curl_easy_setopt(curlw->GetHandle(), CURLOPT_WRITEFUNCTION,
+ curl_easy_setopt(curlw.GetHandle(), CURLOPT_WRITEFUNCTION,
write_streamed_data);
- curl_easy_setopt(curlw->GetHandle(), CURLOPT_WRITEDATA, this);
+ curl_easy_setopt(curlw.GetHandle(), CURLOPT_WRITEDATA, this);
- globalFiles = &files;
- curl_easy_setopt(curlw->GetHandle(), CURLOPT_POSTFIELDS, dest);
- curl_easy_setopt(curlw->GetHandle(), CURLOPT_POSTFIELDSIZE, destlen);
- curl_easy_setopt(curlw->GetHandle(), CURLOPT_NOPROGRESS, 0L);
- curl_easy_setopt(curlw->GetHandle(), CURLOPT_PROGRESSFUNCTION, progress_func);
- curl_easy_setopt(curlw->GetHandle(), CURLOPT_PROGRESSDATA, this);
+ curl_easy_setopt(curlw.GetHandle(), CURLOPT_POSTFIELDS, &dest[0]);
+ curl_easy_setopt(curlw.GetHandle(), CURLOPT_POSTFIELDSIZE, destlen);
+ curl_easy_setopt(curlw.GetHandle(), CURLOPT_NOPROGRESS, 0L);
+ curl_easy_setopt(curlw.GetHandle(), CURLOPT_PROGRESSFUNCTION, progress_func);
+ curl_easy_setopt(curlw.GetHandle(), CURLOPT_PROGRESSDATA, this);
- res = curl_easy_perform(curlw->GetHandle());
- free(dest);
- free(buf);
+ res = curl_easy_perform(curlw.GetHandle());
/* always cleanup */
- delete curlw;
- curlw = NULL;
if (res != CURLE_OK) {
- LOG_ERROR("Curl cleanup error: %s", curl_easy_strerror(res));
+ LOG_ERROR("Curl error: %s", curl_easy_strerror(res));
return false;
}
return true;
diff --git a/src/downloader/lib/src/Downloader/Rapid/Sdp.h b/src/downloader/lib/src/Downloader/Rapid/Sdp.h
index 3ccf217..6ec8360 100644
--- a/src/downloader/lib/src/Downloader/Rapid/Sdp.h
+++ b/src/downloader/lib/src/Downloader/Rapid/Sdp.h
@@ -3,13 +3,14 @@
#ifndef _SDP_H
#define _SDP_H
+#include "FileSystem/FileData.h"
+
#include <string>
#include <list>
#define LENGTH_SIZE 4
class IDownload;
-class FileData;
class CFile;
class CSdp
@@ -17,14 +18,18 @@ class CSdp
public:
CSdp(const std::string& shortname, const std::string& md5,
const std::string& name, const std::string& depends,
- const std::string& url);
+ const std::string& baseUrl);
~CSdp();
/**
download a game, we already know the host where to download from + the
md5 of the sdp file
we have to download the sdp + parse it + download associated files
*/
- bool download(IDownload* download);
+ bool download(IDownload* dl);
+ /**
+ download the sdp file if it doesn't exist yet
+ */
+ bool downloadSelf(IDownload* dl);
/**
returns md5 of a repo
*/
@@ -55,8 +60,8 @@ public:
}
IDownload* m_download;
bool downloadInitialized;
- std::list<FileData*>::iterator list_it;
- std::list<FileData*>* globalFiles; // list with all files of an sdp
+ std::list<FileData>::iterator list_it;
+ std::list<FileData> files; // list with all files of an sdp
CFile* file_handle;
std::string file_name;
@@ -94,13 +99,14 @@ private:
T 192.168.1.2:33202 -> 94.23.170.70:80 [AP]
......zL..c`..`d.....K.n/....
*/
- bool downloadStream(const std::string& url, std::list<FileData*> files);
+ bool downloadStream();
+
std::string name;
std::string md5;
std::string shortname;
- std::string url;
- std::string filename;
+ std::string baseUrl;
std::string depends;
+ std::string sdpPath;
bool downloaded;
};
diff --git a/src/downloader/lib/src/FileSystem/File.cpp b/src/downloader/lib/src/FileSystem/File.cpp
index 8bce303..4687f7c 100644
--- a/src/downloader/lib/src/FileSystem/File.cpp
+++ b/src/downloader/lib/src/FileSystem/File.cpp
@@ -216,7 +216,7 @@ int CFile::Write(const char* buf, int bufsize, int piece)
int CFile::Seek(unsigned long pos, int piece)
{
- assert(piece <= (int)pieces.size());
+ assert(piece < (int)pieces.size());
if (piece >= 0) { // adjust position relative to piece pos
pos = this->piecesize * piece + pos;
}
@@ -273,7 +273,7 @@ int CFile::GetPiecesSize(std::vector<unsigned int> pieces) const
int CFile::GetPieceSize(int piece) const
{
if (piece >= 0) {
- assert(piece <= (int)pieces.size());
+ assert(piece < (int)pieces.size());
if ((int)pieces.size() - 1 == piece) // last piece
return size - (piecesize * ((int)pieces.size() - 1));
// LOG("GetPieceSize piece %d, pieces.size() %d piecesize: %d size %d
@@ -289,7 +289,7 @@ int CFile::GetPieceSize(int piece) const
long CFile::GetPiecePos(int piece) const
{
- assert(piece <= (int)pieces.size());
+ assert(piece < (int)pieces.size());
if (piece >= 0)
return pieces[piece].pos;
return curpos;
diff --git a/src/downloader/lib/src/FileSystem/FileSystem.cpp b/src/downloader/lib/src/FileSystem/FileSystem.cpp
index 5b3650a..ef6d613 100644
--- a/src/downloader/lib/src/FileSystem/FileSystem.cpp
+++ b/src/downloader/lib/src/FileSystem/FileSystem.cpp
@@ -81,8 +81,14 @@ bool CFileSystem::fileIsValid(const FileData* mod,
return true;
}
-bool CFileSystem::parseSdp(const std::string& filename,
- std::list<FileData*>& files)
+std::string getMD5fromFilename(const std::string& path)
+{
+ const size_t start = path.rfind(PATH_DELIMITER) + 1;
+ const size_t end = path.find(".", start);
+ return path.substr(start, end-start);
+}
+
+bool CFileSystem::parseSdp(const std::string& filename, std::list<FileData>& files)
{
char c_name[255];
unsigned char c_md5[16];
@@ -94,16 +100,21 @@ bool CFileSystem::parseSdp(const std::string& filename,
gzFile in = gzdopen(fileno(f), "rb");
if (in == Z_NULL) {
LOG_ERROR("Could not open %s", filename.c_str());
+ fclose(f);
return false;
}
files.clear();
+ HashMD5 sdpmd5;
+ sdpmd5.Init();
while (true) {
+
if (!gzread(in, &length, 1)) {
if (gzeof(in)) {
break;
}
LOG_ERROR("Unexpected eof in %s", filename.c_str());
gzclose(in);
+ fclose(f);
return false;
}
if (!((gzread(in, &c_name, length)) && (gzread(in, &c_md5, 16)) &&
@@ -113,15 +124,31 @@ bool CFileSystem::parseSdp(const std::string& filename,
fclose(f);
return false;
}
- FileData* f = new FileData;
- f->name = std::string(c_name, length);
- memcpy(f->md5, &c_md5, 16);
- memcpy(f->crc32, &c_crc32, 4);
- f->size = parse_int32(c_size);
- files.push_back(f);
+ FileData fd;
+ fd.name = std::string(c_name, length);
+ memcpy(fd.md5, &c_md5, 16);
+ memcpy(fd.crc32, &c_crc32, 4);
+ fd.size = parse_int32(c_size);
+ files.push_back(fd);
+
+ HashMD5 nameMd5;
+ nameMd5.Init();
+ nameMd5.Update(fd.name.data(), fd.name.size());
+ nameMd5.Final();
+ assert(nameMd5.getSize() == 16);
+ assert(sizeof(fd.md5) == 16);
+ sdpmd5.Update((const char*)nameMd5.Data(), nameMd5.getSize());
+ sdpmd5.Update((const char*)&fd.md5[0], sizeof(fd.md5));
}
gzclose(in);
fclose(f);
+ sdpmd5.Final();
+ const std::string filehash = getMD5fromFilename(filename);
+ if (filehash != sdpmd5.toString()) {
+ LOG_ERROR("%s is invalid, deleted (%s vs %s)", filename.c_str(), filehash.c_str(), sdpmd5.toString().c_str());
+ removeFile(filename);
+ return false;
+ }
LOG_DEBUG("Parsed %s with %d files", filename.c_str(), (int)files.size());
return true;
}
@@ -475,21 +502,61 @@ bool CFileSystem::parseTorrent(const char* data, int size, IDownload* dl)
bool CFileSystem::dumpSDP(const std::string& filename)
{
- std::list<FileData*> files;
- files.clear();
+ std::list<FileData> files;
if (!parseSdp(filename, files))
return false;
LOG_INFO("md5 (filename in pool) crc32 size filename");
std::list<FileData*>::iterator it;
HashMD5 md5;
- for (it = files.begin(); it != files.end(); ++it) {
- md5.Set((*it)->md5, sizeof((*it)->md5));
- LOG_INFO("%s %.8X %8d %s", md5.toString().c_str(), (*it)->crc32,
- (*it)->size, (*it)->name.c_str());
+ for (const FileData& fd: files) {
+ md5.Set(fd.md5, sizeof(fd.md5));
+ LOG_INFO("%s %.8X %8d %s", md5.toString().c_str(), fd.crc32,
+ fd.size, fd.name.c_str());
}
return true;
}
+bool CFileSystem::validateSDP(const std::string& sdpPath)
+{
+ LOG_DEBUG("CFileSystem::validateSDP() ...");
+ if (!fileExists(sdpPath)){
+ LOG_ERROR("SDP file doesn't exist: %s", sdpPath.c_str());
+ return false;
+ }
+
+ std::list<FileData> files;
+ if (!parseSdp(sdpPath, files)) {// parse downloaded file
+ LOG_ERROR("Removing invalid SDP file: %s", sdpPath.c_str());
+ if (!removeFile(sdpPath)) {
+ LOG_ERROR("Failed removing %s, aborting", sdpPath.c_str());
+ return false;
+ }
+ return false;
+ }
+
+ bool valid = true;
+ for (FileData& fd : files) {
+
+ std::string filePath;
+ HashMD5 fileMd5;
+ fileMd5.Set(fd.md5, sizeof(fd.md5));
+ getPoolFilename(fileMd5.toString(), filePath);
+ if(!fileExists(filePath)) {
+ valid = false;
+ LOG_INFO("Missing file: %s", filePath.c_str());
+ } else if (!fileIsValid(&fd, filePath)) {
+ valid = false;
+ LOG_INFO("Removing invalid file: %s", filePath.c_str());
+ if (!removeFile(filePath)) {
+ LOG_ERROR("Failed removing %s, aborting", filePath.c_str());
+ return false;
+ }
+ }
+ }
+ LOG_DEBUG("CFileSystem::validateSDP() done");
+ return valid;
+}
+
bool CFileSystem::extractEngine(const std::string& filename,
const std::string& version)
{
diff --git a/src/downloader/lib/src/FileSystem/FileSystem.h b/src/downloader/lib/src/FileSystem/FileSystem.h
index 143fd02..9030c5a 100644
--- a/src/downloader/lib/src/FileSystem/FileSystem.h
+++ b/src/downloader/lib/src/FileSystem/FileSystem.h
@@ -3,12 +3,13 @@
#ifndef FILE_SYSTEM_H
#define FILE_SYSTEM_H
+#include "FileData.h"
+
#include <list>
#include <string>
class SRepository;
class CRepo;
-class FileData;
class IDownload;
#ifdef WIN32
@@ -30,7 +31,7 @@ public:
/**
parses the file for a mod and creates
*/
- bool parseSdp(const std::string& filename, std::list<FileData*>& files);
+ bool parseSdp(const std::string& filename, std::list<FileData>& files);
/**
* Validates a pool-file, (checks the md5)
*/
@@ -87,6 +88,10 @@ public:
*/
bool dumpSDP(const std::string& filename);
/**
+ * validates the given .sdp
+ */
+ bool validateSDP(const std::string& filename);
+ /**
* extracts a 7z file to dstdir
*/
bool extract(const std::string& filename, const std::string& dstdir,
diff --git a/src/downloader/lib/src/FileSystem/HashMD5.h b/src/downloader/lib/src/FileSystem/HashMD5.h
index cb14bf1..a3d6795 100644
--- a/src/downloader/lib/src/FileSystem/HashMD5.h
+++ b/src/downloader/lib/src/FileSystem/HashMD5.h
@@ -16,8 +16,8 @@ public:
bool Set(const unsigned char* data, int size);
unsigned char get(int pos) const;
static std::string CalculateHash(const char* data, const int size);
+ const unsigned char* Data() const { return &mdContext.digest[0]; }
-protected:
int getSize() const;
private:
diff --git a/src/downloader/lib/src/lib/CMakeLists.txt b/src/downloader/lib/src/lib/CMakeLists.txt
index d4ed54a..459d2c5 100644
--- a/src/downloader/lib/src/lib/CMakeLists.txt
+++ b/src/downloader/lib/src/lib/CMakeLists.txt
@@ -1,9 +1,9 @@
if(PRD_ARCHIVE_SUPPORT)
- subdirs(7z)
+ add_subdirectory(7z)
if (NOT MINIZIP_FOUND)
- subdirs(minizip)
+ add_subdirectory(minizip)
endif()
endif()
-subdirs(md5)
-subdirs(bencode)
-subdirs(sha1)
+add_subdirectory(md5)
+add_subdirectory(bencode)
+add_subdirectory(sha1)
diff --git a/src/downloader/lib/src/lsl/lslunitsync/CMakeLists.txt b/src/downloader/lib/src/lsl/lslunitsync/CMakeLists.txt
index 569b32c..54d1fed 100644
--- a/src/downloader/lib/src/lsl/lslunitsync/CMakeLists.txt
+++ b/src/downloader/lib/src/lsl/lslunitsync/CMakeLists.txt
@@ -32,7 +32,7 @@ endif()
if (UNIX AND NOT MINGW AND NOT APPLE)
FIND_LIBRARY(RT_LIBRARY rt)
endif()
-TARGET_LINK_LIBRARIES(lsl-unitsync lsl-utils ${Boost_LIBRARIES} ${PNG_LIBRARY} ${X11_LIBRARIES} ${CMAKE_DL_LIBS} ${RT_LIBRARY})
+TARGET_LINK_LIBRARIES(lsl-unitsync lsl-utils ${Boost_LIBRARIES} ${PNG_LIBRARY} ${X11_LIBRARIES} ${CMAKE_DL_LIBS} ${RT_LIBRARY} ${PRD_JSONCPP_LIBRARIES})
target_include_directories(lsl-unitsync
PRIVATE ${pr-downloader_SOURCE_DIR}/src/lsl
PRIVATE ${pr-downloader_SOURCE_DIR}/src/lib
diff --git a/src/downloader/lib/src/lsl/lslunitsync/c_api.cpp b/src/downloader/lib/src/lsl/lslunitsync/c_api.cpp
index 94e9d9c..8be330f 100644
--- a/src/downloader/lib/src/lsl/lslunitsync/c_api.cpp
+++ b/src/downloader/lib/src/lsl/lslunitsync/c_api.cpp
@@ -562,6 +562,12 @@ unsigned int UnitsyncLib::GetPrimaryModChecksumFromName(const std::string& name)
return m_get_primary_mod_checksum_from_name(name.c_str());
}
+unsigned int UnitsyncLib::GetMapChecksumFromName(const std::string& name)
+{
+ InitLib(m_get_map_checksum_from_name);
+ return m_get_map_checksum_from_name(name.c_str());
+}
+
UnitsyncLib::StringVector UnitsyncLib::GetModDeps(int index)
{
const int count = GetPrimaryModArchiveCount(index);
diff --git a/src/downloader/lib/src/lsl/lslunitsync/c_api.h b/src/downloader/lib/src/lsl/lslunitsync/c_api.h
index a0a0b38..5e659c9 100644
--- a/src/downloader/lib/src/lsl/lslunitsync/c_api.h
+++ b/src/downloader/lib/src/lsl/lslunitsync/c_api.h
@@ -165,6 +165,7 @@ public:
int GetPrimaryModArchiveCount(int index);
std::string GetPrimaryModArchiveList(int arnr);
unsigned int GetPrimaryModChecksumFromName(const std::string& name);
+ unsigned int GetMapChecksumFromName(const std::string& name);
StringVector GetModDeps(int index);
StringVector GetSides(const std::string& modName);
diff --git a/src/downloader/lib/src/lsl/lslunitsync/unitsync.cpp b/src/downloader/lib/src/lsl/lslunitsync/unitsync.cpp
index 31df17a..27e2bcf 100644
--- a/src/downloader/lib/src/lsl/lslunitsync/unitsync.cpp
+++ b/src/downloader/lib/src/lsl/lslunitsync/unitsync.cpp
@@ -162,52 +162,38 @@ void Unitsync::PopulateArchiveList()
const int numMaps = susynclib().GetMapCount();
for (int i = 0; i < numMaps; i++) {
std::string name, archivename;
- unsigned int hash;
try {
const int count = susynclib().GetMapArchiveCount(i);
if (count > 0) {
archivename = susynclib().GetMapArchiveName(0);
}
name = susynclib().GetMapName(i);
- hash = susynclib().GetMapChecksum(i);
//PrefetchMap( name ); // DEBUG
} catch (...) {
continue;
}
- try {
- assert(!name.empty());
- m_maps_list[name] = LSL::Util::ToUIntString(hash);
- if (!archivename.empty())
- m_maps_archive_name[name] = archivename;
- m_map_array.push_back(name);
- } catch (...) {
- LslError("Found map with hash collision: %s hash: %d", name.c_str(), hash);
- }
+ assert(!name.empty());
+ if (!archivename.empty())
+ m_maps_archive_name[name] = archivename;
+ m_map_array.push_back(name);
FetchUnitsyncErrors(name);
}
const int numMods = susynclib().GetPrimaryModCount();
for (int i = 0; i < numMods; i++) {
std::string name, archivename;
- unsigned int hash;
try {
const int count = susynclib().GetPrimaryModArchiveCount(i);
if (count > 0) {
archivename = susynclib().GetPrimaryModArchive(i);
}
name = GetGameInfo(i, "name");
- hash = susynclib().GetPrimaryModChecksumFromName(name);
} catch (...) {
continue;
}
- try {
- assert(!name.empty());
- m_mods_list[name] = LSL::Util::ToUIntString(hash);
- if (!archivename.empty())
- m_mods_archive_name[name] = archivename;
- m_mod_array.push_back(name);
- } catch (...) {
- LslError("Found game with hash collision: %s hash: %s", name.c_str(), hash);
- }
+ assert(!name.empty());
+ if (!archivename.empty())
+ m_mods_archive_name[name] = archivename;
+ m_mod_array.push_back(name);
FetchUnitsyncErrors(name);
}
m_unsorted_mod_array = m_mod_array;
@@ -248,18 +234,31 @@ StringVector Unitsync::GetGameList() const
return m_mod_array;
}
-bool Unitsync::GameExists(const std::string& gamename, const std::string& hash) const
+bool Unitsync::GameExists(const std::string& gamename, const std::string& hash)
{
TRY_LOCK(false)
- LocalArchivesVector::const_iterator itor = m_mods_list.find(gamename);
- if (itor == m_mods_list.end() || itor->second.empty())
+ LocalArchivesVector::const_iterator itor = m_mods_archive_name.find(gamename);
+ if (itor == m_mods_archive_name.end() || itor->second.empty())
return false;
assert(!itor->second.empty()); //empty hashes are invalid
if (hash.empty())
return true;
- return itor->second == hash;
+ return GetGameHash(gamename) == hash;
+}
+
+std::string Unitsync::GetGameHash(const std::string& name)
+{
+ LocalArchivesVector::const_iterator itor = m_mods_list.find(name);
+ if (itor != m_mods_list.end()) {
+ return itor->second;
+ }
+ const unsigned int modhash = susynclib().GetPrimaryModChecksumFromName(name);
+ assert(modhash>0);
+ const std::string strhash = LSL::Util::ToUIntString(modhash);
+ m_mods_list[name] = strhash;
+ return strhash;
}
UnitsyncGame Unitsync::GetGame(const std::string& gamename)
@@ -267,7 +266,9 @@ UnitsyncGame Unitsync::GetGame(const std::string& gamename)
UnitsyncGame m;
TRY_LOCK(m);
m.name = gamename;
- m.hash = m_mods_list[gamename];
+ m.hash = GetGameHash(gamename);
+ assert(m.hash != "0");
+ assert(!m.hash.empty());
return m;
}
@@ -277,7 +278,9 @@ UnitsyncGame Unitsync::GetGame(int index)
UnitsyncGame m;
TRY_LOCK(m);
m.name = m_mod_array[index];
- m.hash = m_mods_list[m.name];
+ m.hash = GetGameHash(m.name);
+ assert(m.hash != "0");
+ assert(!m.hash.empty());
return m;
}
@@ -300,17 +303,31 @@ StringVector Unitsync::GetGameValidMapList(const std::string& gamename) const
return ret;
}
-bool Unitsync::MapExists(const std::string& mapname, const std::string& hash) const
+bool Unitsync::MapExists(const std::string& mapname, const std::string& hash)
{
assert(!mapname.empty());
TRY_LOCK(false)
- LocalArchivesVector::const_iterator itor = m_maps_list.find(mapname);
- if (itor == m_maps_list.end())
+ LocalArchivesVector::const_iterator itor = m_maps_archive_name.find(mapname);
+ if (itor == m_maps_archive_name.end())
return false;
assert(!itor->second.empty()); //empty hashes are invalid
if (hash.empty())
return true;
- return itor->second == hash;
+
+ return GetMapHash(mapname) == hash;
+}
+
+std::string Unitsync::GetMapHash(const std::string& name)
+{
+ LocalArchivesVector::const_iterator itor = m_maps_list.find(name);
+ if (itor != m_maps_list.end()) {
+ return itor->second;
+ }
+ const unsigned int modhash = susynclib().GetMapChecksumFromName(name);
+ assert(modhash > 0);
+ const std::string strhash = LSL::Util::ToUIntString(modhash);
+ m_maps_list[name] = strhash;
+ return strhash;
}
UnitsyncMap Unitsync::GetMap(int index)
@@ -320,7 +337,7 @@ UnitsyncMap Unitsync::GetMap(int index)
if (index < 0)
return m;
m.name = m_map_array[index];
- m.hash = m_maps_list[m.name];
+ m.hash = GetMapHash(m.name);
m.info = _GetMapInfoEx(m.name);
assert(!m.hash.empty());
return m;
@@ -411,7 +428,7 @@ UnitsyncMap Unitsync::GetMap(const std::string& mapname)
LSL_THROWF(unitsync, "Map does not exist: %s", mapname.c_str());
}
m.name = m_map_array[i];
- m.hash = m_maps_list[m.name];
+ m.hash = GetMapHash(mapname);
m.info = _GetMapInfoEx(m.name);
assert(!m.hash.empty());
return m;
@@ -427,6 +444,7 @@ GameOptions Unitsync::GetGameOptions(const std::string& name)
if (m_game_gameoptions.find(name) != m_game_gameoptions.end()) {
return m_game_gameoptions[name];
}
+ GetGameHash(name);
const std::string filename = GetGameOptionsPath(name);
if (!lslcache.Get(filename, ret)) {
PrefetchGame(name);
@@ -455,6 +473,7 @@ StringVector Unitsync::GetSides(const std::string& gamename)
if (!GameExists(gamename))
return ret;
TRY_LOCK(ret);
+ GetGameHash(gamename);
const std::string cachefile = GetSidesCachePath(gamename);
if (!lslcache.Get(cachefile, ret) && (GameExists(gamename))) { // cache file failed, try from lsl
@@ -911,6 +930,7 @@ void Unitsync::PrefetchMap(const std::string& mapname)
FetchUnitsyncErrors(mapname);
+ GetMapHash(mapname);
const int index = Util::IndexInSequence(m_unsorted_map_array, mapname);
ASSERT_EXCEPTION(index >= 0, "Map not found");
@@ -949,6 +969,7 @@ void Unitsync::PrefetchGame(const std::string& gamename)
{
assert(!gamename.empty());
susynclib().SetCurrentMod(gamename);
+ GetGameHash(gamename);
{
int count = susynclib().GetModOptionCount(gamename);
GameOptions opt;
diff --git a/src/downloader/lib/src/lsl/lslunitsync/unitsync.h b/src/downloader/lib/src/lsl/lslunitsync/unitsync.h
index ba6bf8c..3695c3c 100644
--- a/src/downloader/lib/src/lsl/lslunitsync/unitsync.h
+++ b/src/downloader/lib/src/lsl/lslunitsync/unitsync.h
@@ -51,7 +51,7 @@ public:
~Unitsync();
StringVector GetGameList() const;
- bool GameExists(const std::string& gamename, const std::string& hash = "") const;
+ bool GameExists(const std::string& gamename, const std::string& hash = "");
UnitsyncGame GetGame(const std::string& gamename);
UnitsyncGame GetGame(int index);
@@ -60,7 +60,7 @@ public:
StringVector GetMapList() const;
StringVector GetGameValidMapList(const std::string& gamename) const;
- bool MapExists(const std::string& mapname, const std::string& hash = "") const;
+ bool MapExists(const std::string& mapname, const std::string& hash = "");
UnitsyncMap GetMap(const std::string& mapname);
UnitsyncMap GetMap(int index);
@@ -131,6 +131,8 @@ public:
std::string GetUnitsCacheFilePath(const std::string& gamename) const;
private:
+ std::string GetMapHash(const std::string& name);
+ std::string GetGameHash(const std::string& name);
bool GetImageFromCache(const std::string& cachefile, UnitsyncImage& img, ImageType imgtype);
UnitsyncImage GetImageFromUS(const std::string& mapname, const MapInfo& info, ImageType imgtype);
diff --git a/src/downloader/lib/src/main.cpp b/src/downloader/lib/src/main.cpp
index b7ab968..90b558f 100644
--- a/src/downloader/lib/src/main.cpp
+++ b/src/downloader/lib/src/main.cpp
@@ -23,6 +23,7 @@ enum {
WIDGET_SEARCH,
FILESYSTEM_WRITEPATH,
FILESYSTEM_DUMPSDP,
+ FILESYSTEM_VALIDATESDP,
DOWNLOAD_MAP,
DOWNLOAD_GAME,
DOWNLOAD_ENGINE,
@@ -38,6 +39,7 @@ static struct option long_options[] = {
{"rapid-validate", 0, 0, RAPID_VALIDATE},
{"delete", 0, 0, RAPID_VALIDATE_DELETE},
{"dump-sdp", 1, 0, FILESYSTEM_DUMPSDP},
+ {"validate-sdp", 1, 0, FILESYSTEM_VALIDATESDP},
{"http-download", 1, 0, HTTP_DOWNLOAD},
{"http-search", 1, 0, HTTP_SEARCH},
{"download-map", 1, 0, DOWNLOAD_MAP},
@@ -142,6 +144,10 @@ int main(int argc, char** argv)
DownloadDumpSDP(optarg);
break;
}
+ case FILESYSTEM_VALIDATESDP: {
+ ValidateSDP(optarg);
+ break;
+ }
case FILESYSTEM_WRITEPATH: {
DownloadSetConfig(CONFIG_FILESYSTEM_WRITEPATH, optarg);
break;
diff --git a/src/downloader/lib/src/pr-downloader.cpp b/src/downloader/lib/src/pr-downloader.cpp
index 80a8258..df56c8e 100644
--- a/src/downloader/lib/src/pr-downloader.cpp
+++ b/src/downloader/lib/src/pr-downloader.cpp
@@ -145,6 +145,7 @@ bool DownloadGetInfo(int id, downloadInfo& info)
if (dl != NULL) {
strncpy(info.filename, dl->name.c_str(),
NAME_LEN - 1); // -1 because of 0 termination
+ info.cat = dl->cat;
return true;
}
return false;
@@ -278,6 +279,11 @@ bool DownloadDumpSDP(const char* path)
return fileSystem->dumpSDP(path);
}
+bool ValidateSDP(const char* path)
+{
+ return fileSystem->validateSDP(path);
+}
+
void DownloadDisableLogging(bool disableLogging)
{
LOG_DISABLE(disableLogging);
@@ -287,8 +293,8 @@ void DownloadDisableLogging(bool disableLogging)
char* CalcHash(const char* str, int size, int type)
{
const unsigned char* hash;
+ MD5_CTX ctx;
if (type == 0) {
- MD5_CTX ctx;
MD5Init(&ctx);
MD5Update(&ctx, (unsigned char*)str, size);
MD5Final(&ctx);
@@ -303,3 +309,8 @@ char* CalcHash(const char* str, int size, int type)
ret[encoded.size()] = '\0';
return ret;
}
+
+void SetAbortDownloads(bool value)
+{
+ IDownloader::SetAbortDownloads(value);
+}
diff --git a/src/downloader/lib/src/pr-downloader.h b/src/downloader/lib/src/pr-downloader.h
index 844d216..ef50d71 100644
--- a/src/downloader/lib/src/pr-downloader.h
+++ b/src/downloader/lib/src/pr-downloader.h
@@ -81,6 +81,11 @@ extern bool DownloadRapidValidate(bool deletebroken);
extern bool DownloadDumpSDP(const char* path);
/**
+* validate sdp files
+*/
+extern bool ValidateSDP(const char* path);
+
+/**
* control printing to stdout
*/
extern void DownloadDisableLogging(bool disableLogging);
@@ -95,4 +100,11 @@ extern void SetDownloadListener(IDownloaderProcessUpdateListener listener);
* 0 - md5
*/
extern char* CalcHash(const char* str, int size, int type);
+
+/**
+* abort all downloads - must be called before shutting down,
+* all downloads must return before calling shutdown
+*/
+extern void SetAbortDownloads(bool value);
+
#endif
diff --git a/src/downloader/lib/test/CMakeLists.txt b/src/downloader/lib/test/CMakeLists.txt
index 862a504..c1165a2 100644
--- a/src/downloader/lib/test/CMakeLists.txt
+++ b/src/downloader/lib/test/CMakeLists.txt
@@ -4,7 +4,7 @@ FIND_PACKAGE(Boost 1.35.0 COMPONENTS unit_test_framework)
IF(NOT Boost_FOUND)
Message(STATUS "Note: Unit tests will not be built: Boost::test library was not found")
Else()
- add_custom_target(check ${CMAKE_CTEST_COMMAND} --output-on-failure)
+ add_custom_target(checkDownloader ${CMAKE_CTEST_COMMAND} --output-on-failure)
If (NOT (WIN32 OR Boost_USE_STATIC_LIBS))
#Win32 tests links static
add_definitions(-DBOOST_TEST_DYN_LINK)
@@ -26,18 +26,19 @@ Else()
target_include_directories(prd_test
PRIVATE ${Boost_INCLUDE_DIRS}
- )
+ )
# target_include_directories(libSpringLobby_test
# PRIVATE ${libSpringLobby_SOURCE_DIR}/src
# )
-
################################################################################
### libSpringLobby
+if (PRD_ENABLE_LSL)
+
SET(basic_testSrc
- ${CMAKE_CURRENT_SOURCE_DIR}/lsl/basic.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/lsl/basic.cpp
${CMAKE_CURRENT_SOURCE_DIR}/lsl/usync.cpp
)
@@ -53,6 +54,8 @@ IF( NOT WIN32 )
TARGET_LINK_LIBRARIES(libSpringLobby_test X11 )
ENDIF()
+endif(PRD_ENABLE_LSL)
+
################################################################################
### swig
diff --git a/src/gui/chatpanel.cpp b/src/gui/chatpanel.cpp
index 00e5e04..4eca171 100644
--- a/src/gui/chatpanel.cpp
+++ b/src/gui/chatpanel.cpp
@@ -935,7 +935,7 @@ bool ChatPanel::Say(const wxString& message) //FIXME: remove all parsing / token
}
while (lines.HasMoreTokens()) {
wxString line = lines.GetNextToken();
- wxLogMessage(_T( "line: %s" ), line.c_str());
+ wxLogDebug(_T( "line: %s" ), line.c_str());
if (line == "/help") {
ui().ConsoleHelp();
diff --git a/src/gui/singleplayertab.cpp b/src/gui/singleplayertab.cpp
index d42a600..8685fed 100644
--- a/src/gui/singleplayertab.cpp
+++ b/src/gui/singleplayertab.cpp
@@ -343,6 +343,9 @@ bool SinglePlayerTab::ValidSetup() const
void SinglePlayerTab::OnMapSelect(wxCommandEvent& /*unused*/)
{
unsigned int index = (unsigned int)m_map_pick->GetCurrentSelection();
+
+ if (index >= m_map_pick->GetCount() - 1) { return; }
+
SetMap(index);
}
@@ -350,6 +353,9 @@ void SinglePlayerTab::OnMapSelect(wxCommandEvent& /*unused*/)
void SinglePlayerTab::OnModSelect(wxCommandEvent& /*unused*/)
{
unsigned int index = (unsigned int)m_mod_pick->GetCurrentSelection();
+
+ if (index >= m_mod_pick->GetCount() - 1) { return; }
+
size_t num_bots = m_battle.GetNumBots();
SetMod(index);
if (num_bots != m_battle.GetNumBots())
@@ -358,9 +364,13 @@ void SinglePlayerTab::OnModSelect(wxCommandEvent& /*unused*/)
void SinglePlayerTab::OnEngineSelect(wxCommandEvent& /*event*/)
{
- SlPaths::SetUsedSpringIndex(STD_STRING(m_engine_pick->GetString(m_engine_pick->GetSelection())));
+ int index = m_engine_pick->GetSelection();
+
+ if (static_cast<unsigned int>(index) >= (m_engine_pick->GetCount() - 1)) { return; }
+
+ SlPaths::SetUsedSpringIndex(STD_STRING(m_engine_pick->GetString(index)));
LSL::usync().ReloadUnitSyncLib();
- m_battle.SetEngineVersion(STD_STRING(m_engine_pick->GetString(m_engine_pick->GetSelection())));
+ m_battle.SetEngineVersion(STD_STRING(m_engine_pick->GetString(index)));
}
void SinglePlayerTab::OnMapBrowse(wxCommandEvent& /*unused*/)
diff --git a/src/gui/ui.cpp b/src/gui/ui.cpp
index 9e7552b..98cac82 100644
--- a/src/gui/ui.cpp
+++ b/src/gui/ui.cpp
@@ -638,20 +638,6 @@ void Ui::OnUserBattleStatus(User& user)
OnBattleInfoUpdated(*battle, wxEmptyString);
}
-
-void Ui::OnRequestBattleStatus(IBattle& battle)
-{
- if (m_main_win == 0)
- return;
- try {
- if (mw().GetJoinTab().GetBattleRoomTab().GetBattle() == &battle) {
- mw().GetJoinTab().GetBattleRoomTab().GetBattle()->OnRequestBattleStatus();
- }
- } catch (...) {
- }
-}
-
-
void Ui::OnSaidBattle(IBattle& /*battle*/, const wxString& nick, const wxString& msg)
{
if (m_main_win == 0)
diff --git a/src/gui/ui.h b/src/gui/ui.h
index 87fc3e5..df69e2d 100644
--- a/src/gui/ui.h
+++ b/src/gui/ui.h
@@ -88,7 +88,6 @@ public:
void OnJoinedBattle(IBattle& battle);
void OnHostedBattle(IBattle& battle);
void OnUserBattleStatus(User& user);
- void OnRequestBattleStatus(IBattle& battle);
void OnSaidBattle(IBattle& battle, const wxString& nick, const wxString& msg);
diff --git a/src/ibattle.cpp b/src/ibattle.cpp
index 428967b..93f9eef 100644
--- a/src/ibattle.cpp
+++ b/src/ibattle.cpp
@@ -67,19 +67,19 @@ bool IBattle::IsSynced()
LoadGame();
LoadMap();
if (!m_host_game.name.empty() && m_local_game.name != m_host_game.name) {
- wxLogWarning("Not synced: game name doesn't match: '%s' '%s'", m_host_game.name.c_str(), m_local_game.name.c_str());
+ wxLogWarning("Not synced: game name doesn't match host:'%s' local:'%s'", m_host_game.name.c_str(), m_local_game.name.c_str());
return false;
}
if (!m_host_map.name.empty() && m_local_map.name != m_host_map.name) {
- wxLogWarning("Not synced: map name doesn't match: '%s' '%s'", m_host_map.name.c_str(), m_local_map.name.c_str());
+ wxLogWarning("Not synced: map name doesn't match host:'%s' local:'%s'", m_host_map.name.c_str(), m_local_map.name.c_str());
return false;
}
if (!m_host_game.hash.empty() && m_host_game.hash != "0" && m_host_game.hash != m_local_game.hash) {
- wxLogWarning("Not synced: game hash doesn't match: '%s' '%s'", m_host_game.hash.c_str(), m_local_game.hash.c_str());
+ wxLogWarning("Not synced: game hash doesn't match host:'%s' local:'%s'", m_host_game.hash.c_str(), m_local_game.hash.c_str());
return false;
}
if (!m_host_map.hash.empty() && m_host_map.hash != "0" && m_host_map.hash != m_local_map.hash) {
- wxLogWarning("Not synced: map hash doesn't match: '%s' '%s'", m_host_map.hash.c_str(), m_local_map.hash.c_str());
+ wxLogWarning("Not synced: map hash doesn't match host:'%s' local:'%s'", m_host_map.hash.c_str(), m_local_map.hash.c_str());
return false;
}
if (!GameExists()) {
@@ -761,11 +761,12 @@ std::string IBattle::GetHostMapHash() const
void IBattle::SetHostGame(const std::string& gamename, const std::string& hash)
{
assert(hash.empty() || LSL::Util::MakeHashUnsigned(hash) == hash);
- if (m_host_game.name != gamename || m_host_game.hash != hash) {
- m_game_loaded = false;
- m_host_game.name = gamename;
- m_host_game.hash = hash;
+ if ((m_host_game.name == gamename) && (m_host_game.hash == hash)) {
+ return;
}
+ m_game_loaded = false;
+ m_host_game.name = gamename;
+ m_host_game.hash = hash;
}
@@ -775,28 +776,31 @@ void IBattle::SetLocalGame(const LSL::UnitsyncGame& mod)
if (mod.hash.empty()) {
wxLogWarning("empty hash: %s", mod.name);
}
- if (mod.name != m_local_game.name || mod.hash != m_local_game.hash) {
- m_previous_local_game_name = m_local_game.name;
- m_local_game = mod;
- m_game_loaded = true;
+ if ((mod.name == m_local_game.name) && (mod.hash == m_local_game.hash)) {
+ return;
}
+ m_previous_local_game_name = m_local_game.name;
+ m_local_game = mod;
+ m_game_loaded = true;
}
const LSL::UnitsyncGame& IBattle::LoadGame()
{
ASSERT_LOGIC(!m_host_game.name.empty(), "m_host_game.name.empty() is FALSE");
- if (!m_game_loaded) {
- if (GameExists(true)) {
- try {
- SetLocalGame(LSL::usync().GetGame(m_host_game.name));
- bool options_loaded = CustomBattleOptions().loadOptions(LSL::Enum::ModOption, m_host_game.name);
- ASSERT_EXCEPTION(options_loaded, _T("couldn't load the game options"));
- m_game_loaded = true;
- } catch (...) {
- }
- }
+ if (m_game_loaded) {
+ return m_local_game;
+ }
+ if (!GameExists(true)) {
+ wxLogWarning("Game doesn't exist");
+ return m_local_game;
}
+ try {
+ SetLocalGame(LSL::usync().GetGame(m_host_game.name));
+ bool options_loaded = CustomBattleOptions().loadOptions(LSL::Enum::ModOption, m_host_game.name);
+ ASSERT_EXCEPTION(options_loaded, _T("couldn't load the game options"));
+ m_game_loaded = true;
+ } catch (...) { }
return m_local_game;
}
diff --git a/src/serverevents.cpp b/src/serverevents.cpp
index 68e07e8..5ff6d4b 100644
--- a/src/serverevents.cpp
+++ b/src/serverevents.cpp
@@ -715,7 +715,7 @@ void ServerEvents::OnRequestBattleStatus(int battleid)
{
try {
IBattle& battle = m_serv.GetBattle(battleid);
- ui().OnRequestBattleStatus(battle);
+ battle.OnRequestBattleStatus();
} catch (assert_exception) {
}
}
diff --git a/src/socket.cpp b/src/socket.cpp
index 310dc17..ab541df 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -46,7 +46,7 @@ bool GetMac(std::vector<unsigned char>& mac)
DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen); // Get info
if (dwStatus != NO_ERROR)
- return wxEmptyString; // Check status
+ return false; // Check status
for (size_t i = 0; i < sizeof(AdapterInfo); i++) {
mac.resize(AdapterInfo[i].AddressLength);
mac.assign(AdapterInfo[i].Address, AdapterInfo[i].Address + AdapterInfo[i].AddressLength);
diff --git a/src/springlobbyapp.cpp b/src/springlobbyapp.cpp
index f92956d..b6686af 100644
--- a/src/springlobbyapp.cpp
+++ b/src/springlobbyapp.cpp
@@ -31,6 +31,7 @@
#include <wx/utils.h>
#include <wx/wfstream.h>
+#include "stacktrace.h"
#include "springlobbyapp.h"
#include "gui/mainwindow.h"
#include "settings.h"
@@ -222,6 +223,12 @@ int SpringLobbyApp::OnExit()
void SpringLobbyApp::OnFatalException()
{
wxLogError("Fatal exception!");
+
+ StackTrace stackTracer;
+ stackTracer.WalkFromException();
+ auto trace = stackTracer.GetStackTrace();
+
+ wxLogError("Stack trace: " + trace);
}
diff --git a/src/stacktrace.h b/src/stacktrace.h
index 18017bd..d07c955 100644
--- a/src/stacktrace.h
+++ b/src/stacktrace.h
@@ -30,10 +30,12 @@ private:
#else
class StackTrace
{
+public:
wxString GetStackTrace() const
{
return wxEmptyString;
}
+ void WalkFromException() {}
};
#endif
diff --git a/src/sysinfo.cpp b/src/sysinfo.cpp
index 9c30102..0e85a12 100644
--- a/src/sysinfo.cpp
+++ b/src/sysinfo.cpp
@@ -67,6 +67,11 @@ std::string GetSpringlobbyInfo()
getWritePaths(paths);
for (size_t i = 0; i < paths.size(); ++i) {
std::string path = paths[i].m_path;
+
+ if (path.empty()) {
+ continue;
+ }
+
#if defined(__WIN32__) || defined(_MSC_VER)
path = Utf8ToLocalEncoding(path.c_str());
#endif
diff --git a/src/tasserver.cpp b/src/tasserver.cpp
index 46885e3..5e7fd8d 100644
--- a/src/tasserver.cpp
+++ b/src/tasserver.cpp
@@ -724,7 +724,10 @@ void TASServer::ExecuteCommand(const std::string& cmd, const std::string& inpara
m_se->OnPrivateMessageEx(user, user, params);
} else if (cmd == "JOINBATTLE") {
const int id = GetIntParam(params);
- const std::string hash = LSL::Util::MakeHashUnsigned(GetWordParam(params));
+ std::string hash = LSL::Util::MakeHashUnsigned(GetWordParam(params));
+ if (hash == "0") {
+ hash.clear();
+ }
m_battle_id = id;
m_se->OnJoinedBattle(id, hash);
m_se->OnBattleInfoUpdated(m_battle_id);
@@ -985,12 +988,20 @@ void TASServer::SendCmd(const std::string& command, const std::string& param, bo
return;
}
- if (command != "PING") { //don't log PING
- if (send_success)
- wxLogMessage(wxString::Format(_T("sent: %s"), msg.c_str()));
- else
- wxLogMessage(wxString::Format(_T("sending: %s failed"), msg.c_str()));
- }
+ if (command == "PING") return;
+ if (command == "SAY") return;
+ if (command == "SAYEX") return;
+ if (command == "SAYPRIVATE") return;
+ if (command == "SAYPRIVATEEX") return;
+ if (command == "SAYBATTLE") return;
+ if (command == "SAYBATTLEEX") return;
+ if (command == "SAYBATTLEPRIVATE") return;
+ if (command == "SAYBATTLEPRIVATEEX") return;
+
+ if (send_success)
+ wxLogMessage(wxString::Format(_T("sent: %s"), msg.c_str()));
+ else
+ wxLogMessage(wxString::Format(_T("sending: %s failed"), msg.c_str()));
}
void TASServer::SetRelayIngamePassword(const User& user)
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/springlobby.git
More information about the Pkg-games-commits
mailing list