Test using DownloadManager from my mod instead of RequestBroker
The crash when opening saves on mac is definitely something to do with the threading, and i'd rather just test this first to see if it fixes it Copied from my mod almost entirely as-is, with the changes to HTTP.cpp included, also added locks into Download.cpp even though it is probably overkill
This commit is contained in:
parent
49fac83995
commit
40c2ff27b0
@ -6,7 +6,7 @@
|
||||
#include <list>
|
||||
|
||||
#include "Config.h"
|
||||
#include "Singleton.h"
|
||||
#include "common/Singleton.h"
|
||||
|
||||
#include "User.h"
|
||||
#include "UserInfo.h"
|
||||
|
151
src/client/Download.cpp
Normal file
151
src/client/Download.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
#include <stdlib.h>
|
||||
#include "Download.h"
|
||||
#include "DownloadManager.h"
|
||||
#include "http.h"
|
||||
|
||||
Download::Download(std::string uri_, bool keepAlive):
|
||||
http(NULL),
|
||||
keepAlive(keepAlive),
|
||||
downloadData(NULL),
|
||||
downloadSize(0),
|
||||
downloadStatus(0),
|
||||
downloadFinished(false),
|
||||
downloadCanceled(false),
|
||||
downloadStarted(false),
|
||||
postData(""),
|
||||
postDataBoundary(""),
|
||||
userID(""),
|
||||
userSession("")
|
||||
{
|
||||
uri = std::string(uri_);
|
||||
DownloadManager::Ref().AddDownload(this);
|
||||
}
|
||||
|
||||
// called by download thread itself if download was canceled
|
||||
Download::~Download()
|
||||
{
|
||||
if (http && (keepAlive || downloadCanceled))
|
||||
http_async_req_close(http);
|
||||
if (downloadData)
|
||||
free(downloadData);
|
||||
}
|
||||
|
||||
// add post data to a request
|
||||
void Download::AddPostData(std::map<std::string, std::string> data)
|
||||
{
|
||||
postDataBoundary = FindBoundary(data, "");
|
||||
postData = GetMultipartMessage(data, postDataBoundary);
|
||||
}
|
||||
void Download::AddPostData(std::pair<std::string, std::string> data)
|
||||
{
|
||||
std::map<std::string, std::string> postData;
|
||||
postData.insert(data);
|
||||
AddPostData(postData);
|
||||
}
|
||||
|
||||
// add userID and sessionID headers to the download. Must be done after download starts for some reason
|
||||
void Download::AuthHeaders(std::string ID, std::string session)
|
||||
{
|
||||
if (ID != "0")
|
||||
userID = ID;
|
||||
userSession = session;
|
||||
}
|
||||
|
||||
// start the download thread
|
||||
void Download::Start()
|
||||
{
|
||||
if (CheckStarted() || CheckDone())
|
||||
return;
|
||||
http = http_async_req_start(http, uri.c_str(), postData.c_str(), postData.length(), keepAlive ? 1 : 0);
|
||||
// add the necessary headers
|
||||
if (userID.length() || userSession.length())
|
||||
http_auth_headers(http, userID.c_str(), NULL, userSession.c_str());
|
||||
if (postDataBoundary.length())
|
||||
http_add_multipart_header(http, postDataBoundary);
|
||||
DownloadManager::Ref().Lock();
|
||||
downloadStarted = true;
|
||||
DownloadManager::Ref().Unlock();
|
||||
}
|
||||
|
||||
// for persistent connections (keepAlive = true), reuse the open connection to make another request
|
||||
bool Download::Reuse(std::string newuri)
|
||||
{
|
||||
if (!keepAlive || !CheckDone() || CheckCanceled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uri = std::string(newuri);
|
||||
DownloadManager::Ref().Lock();
|
||||
downloadFinished = false;
|
||||
DownloadManager::Ref().Unlock();
|
||||
Start();
|
||||
DownloadManager::Ref().EnsureRunning();
|
||||
return true;
|
||||
}
|
||||
|
||||
// finish the download (if called before the download is done, this will block)
|
||||
char* Download::Finish(int *length, int *status)
|
||||
{
|
||||
if (CheckCanceled())
|
||||
return NULL; // shouldn't happen but just in case
|
||||
while (!CheckDone()); // block
|
||||
DownloadManager::Ref().Lock();
|
||||
downloadStarted = false;
|
||||
if (length)
|
||||
*length = downloadSize;
|
||||
if (status)
|
||||
*status = downloadStatus;
|
||||
char *ret = downloadData;
|
||||
downloadData = NULL;
|
||||
if (!keepAlive)
|
||||
downloadCanceled = true;
|
||||
DownloadManager::Ref().Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// returns the download size and progress (if the download has the correct length headers)
|
||||
void Download::CheckProgress(int *total, int *done)
|
||||
{
|
||||
DownloadManager::Ref().Lock();
|
||||
if (!downloadFinished && http)
|
||||
http_async_get_length(http, total, done);
|
||||
else
|
||||
*total = *done = 0;
|
||||
DownloadManager::Ref().Unlock();
|
||||
}
|
||||
|
||||
// returns true if the download has finished
|
||||
bool Download::CheckDone()
|
||||
{
|
||||
DownloadManager::Ref().Lock();
|
||||
bool ret = downloadFinished;
|
||||
DownloadManager::Ref().Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// returns true if the download was canceled
|
||||
bool Download::CheckCanceled()
|
||||
{
|
||||
DownloadManager::Ref().Lock();
|
||||
bool ret = downloadCanceled;
|
||||
DownloadManager::Ref().Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// returns true if the download is running
|
||||
bool Download::CheckStarted()
|
||||
{
|
||||
DownloadManager::Ref().Lock();
|
||||
bool ret = downloadStarted;
|
||||
DownloadManager::Ref().Unlock();
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
// cancels the download, the download thread will delete the Download* when it finishes (do not use Download in any way after canceling)
|
||||
void Download::Cancel()
|
||||
{
|
||||
DownloadManager::Ref().Lock();
|
||||
downloadCanceled = true;
|
||||
DownloadManager::Ref().Unlock();
|
||||
}
|
47
src/client/Download.h
Normal file
47
src/client/Download.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef DOWNLOAD_H
|
||||
#define DOWNLOAD_H
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class DownloadManager;
|
||||
class Download
|
||||
{
|
||||
std::string uri;
|
||||
void *http;
|
||||
bool keepAlive;
|
||||
|
||||
char *downloadData;
|
||||
int downloadSize;
|
||||
int downloadStatus;
|
||||
|
||||
std::string postData;
|
||||
std::string postDataBoundary;
|
||||
|
||||
std::string userID;
|
||||
std::string userSession;
|
||||
|
||||
volatile bool downloadFinished;
|
||||
volatile bool downloadCanceled;
|
||||
volatile bool downloadStarted;
|
||||
|
||||
public:
|
||||
Download(std::string uri, bool keepAlive = false);
|
||||
~Download();
|
||||
|
||||
void AddPostData(std::map<std::string, std::string> data);
|
||||
void AddPostData(std::pair<std::string, std::string> data);
|
||||
void AuthHeaders(std::string ID, std::string session);
|
||||
void Start();
|
||||
bool Reuse(std::string newuri);
|
||||
char* Finish(int *length, int *status);
|
||||
void Cancel();
|
||||
|
||||
void CheckProgress(int *total, int *done);
|
||||
bool CheckDone();
|
||||
bool CheckCanceled();
|
||||
bool CheckStarted();
|
||||
|
||||
friend class DownloadManager;
|
||||
};
|
||||
|
||||
#endif
|
145
src/client/DownloadManager.cpp
Normal file
145
src/client/DownloadManager.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include "DownloadManager.h"
|
||||
#include "Download.h"
|
||||
#include "http.h"
|
||||
#include "Config.h"
|
||||
#include "Platform.h"
|
||||
|
||||
DownloadManager::DownloadManager():
|
||||
threadStarted(false),
|
||||
lastUsed(time(NULL)),
|
||||
managerRunning(false),
|
||||
managerShutdown(false),
|
||||
downloads(NULL),
|
||||
downloadsAddQueue(NULL)
|
||||
{
|
||||
pthread_mutex_init(&downloadLock, NULL);
|
||||
pthread_mutex_init(&downloadAddLock, NULL);
|
||||
}
|
||||
|
||||
DownloadManager::~DownloadManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DownloadManager::Shutdown()
|
||||
{
|
||||
pthread_mutex_lock(&downloadLock);
|
||||
pthread_mutex_lock(&downloadAddLock);
|
||||
for (std::vector<Download*>::iterator iter = downloads.begin(); iter != downloads.end(); ++iter)
|
||||
{
|
||||
Download *download = (*iter);
|
||||
if (download->http)
|
||||
http_force_close(download->http);
|
||||
download->downloadCanceled = true;
|
||||
delete download;
|
||||
}
|
||||
downloads.clear();
|
||||
downloadsAddQueue.clear();
|
||||
managerShutdown = true;
|
||||
pthread_mutex_unlock(&downloadAddLock);
|
||||
pthread_mutex_unlock(&downloadLock);
|
||||
pthread_join(downloadThread, NULL);
|
||||
}
|
||||
|
||||
//helper function for download
|
||||
TH_ENTRY_POINT void* DownloadManagerHelper(void* obj)
|
||||
{
|
||||
DownloadManager *temp = (DownloadManager*)obj;
|
||||
temp->Update();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DownloadManager::Start()
|
||||
{
|
||||
managerRunning = true;
|
||||
lastUsed = time(NULL);
|
||||
pthread_create(&downloadThread, NULL, &DownloadManagerHelper, this);
|
||||
}
|
||||
|
||||
void DownloadManager::Update()
|
||||
{
|
||||
unsigned int numActiveDownloads;
|
||||
while (!managerShutdown)
|
||||
{
|
||||
pthread_mutex_lock(&downloadAddLock);
|
||||
if (downloadsAddQueue.size())
|
||||
{
|
||||
for (size_t i = 0; i < downloadsAddQueue.size(); i++)
|
||||
{
|
||||
downloads.push_back(downloadsAddQueue[i]);
|
||||
}
|
||||
downloadsAddQueue.clear();
|
||||
}
|
||||
pthread_mutex_unlock(&downloadAddLock);
|
||||
if (downloads.size())
|
||||
{
|
||||
numActiveDownloads = 0;
|
||||
pthread_mutex_lock(&downloadLock);
|
||||
for (size_t i = 0; i < downloads.size(); i++)
|
||||
{
|
||||
Download *download = downloads[i];
|
||||
if (download->downloadCanceled)
|
||||
{
|
||||
if (download->http && download->downloadStarted)
|
||||
http_force_close(download->http);
|
||||
delete download;
|
||||
downloads.erase(downloads.begin()+i);
|
||||
i--;
|
||||
}
|
||||
else if (download->downloadStarted && !download->downloadFinished)
|
||||
{
|
||||
if (http_async_req_status(download->http) != 0)
|
||||
{
|
||||
download->downloadData = http_async_req_stop(download->http, &download->downloadStatus, &download->downloadSize);
|
||||
download->downloadFinished = true;
|
||||
if (!download->keepAlive)
|
||||
download->http = NULL;
|
||||
}
|
||||
lastUsed = time(NULL);
|
||||
numActiveDownloads++;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&downloadLock);
|
||||
}
|
||||
if (time(NULL) > lastUsed+http_timeout*2 && !numActiveDownloads)
|
||||
{
|
||||
pthread_mutex_lock(&downloadLock);
|
||||
managerRunning = false;
|
||||
pthread_mutex_unlock(&downloadLock);
|
||||
return;
|
||||
}
|
||||
Platform::Millisleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
void DownloadManager::EnsureRunning()
|
||||
{
|
||||
pthread_mutex_lock(&downloadLock);
|
||||
if (!managerRunning)
|
||||
{
|
||||
if (threadStarted)
|
||||
pthread_join(downloadThread, NULL);
|
||||
else
|
||||
threadStarted = true;
|
||||
Start();
|
||||
}
|
||||
pthread_mutex_unlock(&downloadLock);
|
||||
}
|
||||
|
||||
void DownloadManager::AddDownload(Download *download)
|
||||
{
|
||||
pthread_mutex_lock(&downloadAddLock);
|
||||
downloadsAddQueue.push_back(download);
|
||||
pthread_mutex_unlock(&downloadAddLock);
|
||||
EnsureRunning();
|
||||
}
|
||||
|
||||
void DownloadManager::Lock()
|
||||
{
|
||||
pthread_mutex_lock(&downloadAddLock);
|
||||
}
|
||||
|
||||
void DownloadManager::Unlock()
|
||||
{
|
||||
pthread_mutex_unlock(&downloadAddLock);
|
||||
}
|
39
src/client/DownloadManager.h
Normal file
39
src/client/DownloadManager.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef DOWNLOADMANAGER_H
|
||||
#define DOWNLOADMANAGER_H
|
||||
#include "common/tpt-thread.h"
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
#include "common/Singleton.h"
|
||||
|
||||
class Download;
|
||||
class DownloadManager : public Singleton<DownloadManager>
|
||||
{
|
||||
private:
|
||||
pthread_t downloadThread;
|
||||
pthread_mutex_t downloadLock;
|
||||
pthread_mutex_t downloadAddLock;
|
||||
bool threadStarted;
|
||||
|
||||
int lastUsed;
|
||||
volatile bool managerRunning;
|
||||
volatile bool managerShutdown;
|
||||
std::vector<Download*> downloads;
|
||||
std::vector<Download*> downloadsAddQueue;
|
||||
|
||||
void Start();
|
||||
public:
|
||||
DownloadManager();
|
||||
~DownloadManager();
|
||||
|
||||
void Shutdown();
|
||||
void Update();
|
||||
void EnsureRunning();
|
||||
|
||||
void AddDownload(Download *download);
|
||||
void RemoveDownload(int id);
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
#endif // DOWNLOADMANAGER_H
|
@ -48,6 +48,7 @@
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "client/DownloadManager.h"
|
||||
#include "Config.h"
|
||||
#include "Misc.h"
|
||||
#include "HTTP.h"
|
||||
@ -79,7 +80,6 @@ typedef SSIZE_T ssize_t;
|
||||
|
||||
char * userAgent;
|
||||
static int http_up = 0;
|
||||
static long http_timeout = 15;
|
||||
static int http_use_proxy = 0;
|
||||
static struct sockaddr_in http_proxy;
|
||||
|
||||
@ -208,6 +208,7 @@ void http_done(void)
|
||||
#ifdef WIN
|
||||
WSACleanup();
|
||||
#endif
|
||||
DownloadManager::Ref().Shutdown();
|
||||
http_up = 0;
|
||||
}
|
||||
|
||||
@ -245,6 +246,12 @@ struct http_ctx
|
||||
void *http_async_req_start(void *ctx, const char *uri, const char *data, int dlen, int keep)
|
||||
{
|
||||
struct http_ctx *cx = (http_ctx *)ctx;
|
||||
if (cx && time(NULL) - cx->last > http_timeout)
|
||||
{
|
||||
http_force_close(ctx);
|
||||
http_async_req_close(ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
if (!ctx)
|
||||
{
|
||||
ctx = calloc(1, sizeof(struct http_ctx));
|
||||
@ -550,14 +557,18 @@ int http_async_req_status(void *ctx)
|
||||
tmp = send(cx->fd, cx->tbuf+cx->tptr, cx->tlen-cx->tptr, 0);
|
||||
if (tmp==PERROR && PERRNO!=PEAGAIN && PERRNO!=PEINTR)
|
||||
goto fail;
|
||||
if (tmp!=PERROR)
|
||||
if (tmp!=PERROR && tmp)
|
||||
{
|
||||
cx->tptr += tmp;
|
||||
if (cx->tptr == cx->tlen)
|
||||
{
|
||||
cx->tptr = 0;
|
||||
cx->tlen = 0;
|
||||
free(cx->tbuf);
|
||||
if (cx->tbuf)
|
||||
{
|
||||
free(cx->tbuf);
|
||||
cx->tbuf = NULL;
|
||||
}
|
||||
cx->state = HTS_RECV;
|
||||
}
|
||||
cx->last = now;
|
||||
@ -569,7 +580,7 @@ int http_async_req_status(void *ctx)
|
||||
tmp = recv(cx->fd, buf, CHUNK, 0);
|
||||
if (tmp==PERROR && PERRNO!=PEAGAIN && PERRNO!=PEINTR)
|
||||
goto fail;
|
||||
if (tmp!=PERROR)
|
||||
if (tmp!=PERROR && tmp)
|
||||
{
|
||||
for (i=0; i<tmp; i++)
|
||||
{
|
||||
@ -623,6 +634,11 @@ char *http_async_req_stop(void *ctx, int *ret, int *len)
|
||||
cx->txd = NULL;
|
||||
cx->txdl = 0;
|
||||
}
|
||||
if (cx->tbuf)
|
||||
{
|
||||
free(cx->tbuf);
|
||||
cx->tbuf = NULL;
|
||||
}
|
||||
if (cx->hbuf)
|
||||
{
|
||||
free(cx->hbuf);
|
||||
@ -675,6 +691,12 @@ void http_async_get_length(void *ctx, int *total, int *done)
|
||||
*total = cx->contlen;
|
||||
}
|
||||
|
||||
void http_force_close(void *ctx)
|
||||
{
|
||||
struct http_ctx *cx = (struct http_ctx*)ctx;
|
||||
cx->state = HTS_DONE;
|
||||
}
|
||||
|
||||
void http_async_req_close(void *ctx)
|
||||
{
|
||||
struct http_ctx *cx = (http_ctx *)ctx;
|
||||
@ -710,7 +732,7 @@ void http_auth_headers(void *ctx, const char *user, const char *pass, const char
|
||||
unsigned char hash[16];
|
||||
struct md5_context md5;
|
||||
|
||||
if (user)
|
||||
if (user && strlen(user))
|
||||
{
|
||||
if (pass)
|
||||
{
|
||||
@ -730,7 +752,7 @@ void http_auth_headers(void *ctx, const char *user, const char *pass, const char
|
||||
http_async_add_header(ctx, "X-Auth-Hash", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
if (session_id)
|
||||
if (session_id && strlen(session_id))
|
||||
{
|
||||
http_async_add_header(ctx, "X-Auth-User-Id", user);
|
||||
http_async_add_header(ctx, "X-Auth-Session-Key", session_id);
|
||||
@ -775,6 +797,9 @@ const char *http_ret_text(int ret)
|
||||
{
|
||||
switch (ret)
|
||||
{
|
||||
case 0:
|
||||
return "Status code 0 (bug?)";
|
||||
|
||||
case 100:
|
||||
return "Continue";
|
||||
case 101:
|
||||
@ -908,6 +933,98 @@ const char *http_ret_text(int ret)
|
||||
return "Unknown Status Code";
|
||||
}
|
||||
}
|
||||
|
||||
// Find the boundary used in the multipart POST request
|
||||
// the boundary is a string that never appears in any of the parts, ex. 'A92'
|
||||
// keeps looking recursively until it finds one
|
||||
std::string FindBoundary(std::map<std::string, std::string> parts, std::string boundary)
|
||||
{
|
||||
// we only look for a-zA-Z0-9 chars
|
||||
unsigned int map[62];
|
||||
size_t blen = boundary.length();
|
||||
std::fill(&map[0], &map[62], 0);
|
||||
for (std::map<std::string, std::string>::iterator iter = parts.begin(); iter != parts.end(); iter++)
|
||||
{
|
||||
// loop through every character in each part and search for the substring, adding 1 to map for every character found (character after the substring)
|
||||
for (ssize_t j = 0; j < (ssize_t)((*iter).second.length())-blen; j++)
|
||||
if (!blen || (*iter).second.substr(j, blen) == boundary)
|
||||
{
|
||||
unsigned char ch = (*iter).second[j+blen];
|
||||
if (ch >= '0' && ch <= '9')
|
||||
map[ch-'0']++;
|
||||
else if (ch >= 'A' && ch <= 'Z')
|
||||
map[ch-'A'+10]++;
|
||||
else if (ch >= 'a' && ch <= 'z')
|
||||
map[ch-'a'+36]++;
|
||||
}
|
||||
}
|
||||
// find which next character occurs the least (preferably it occurs 0 times which means we have a match)
|
||||
unsigned int lowest = 0;
|
||||
for (unsigned int i = 1; i < 62; i++)
|
||||
{
|
||||
if (!map[lowest])
|
||||
break;
|
||||
if (map[i] < map[lowest])
|
||||
lowest = i;
|
||||
}
|
||||
|
||||
// add the least frequent character to our boundary
|
||||
if (lowest < 10)
|
||||
boundary += '0'+lowest;
|
||||
else if (lowest < 36)
|
||||
boundary += 'A'+(lowest-10);
|
||||
else
|
||||
boundary += 'a'+(lowest-36);
|
||||
|
||||
if (map[lowest])
|
||||
return FindBoundary(parts, boundary);
|
||||
else
|
||||
return boundary;
|
||||
}
|
||||
|
||||
// Generates a MIME multipart message to be used in POST requests
|
||||
// see https://en.wikipedia.org/wiki/MIME#Multipart_messages
|
||||
// this function used in Download class, and eventually all http requests
|
||||
std::string GetMultipartMessage(std::map<std::string, std::string> parts, std::string boundary)
|
||||
{
|
||||
std::stringstream data;
|
||||
|
||||
// loop through each part, adding it
|
||||
for (std::map<std::string, std::string>::iterator iter = parts.begin(); iter != parts.end(); iter++)
|
||||
{
|
||||
std::string name = (*iter).first;
|
||||
std::string value = (*iter).second;
|
||||
|
||||
data << "--" << boundary << "\r\n";
|
||||
data << "Content-transfer-encoding: binary" << "\r\n";
|
||||
|
||||
// colon p
|
||||
size_t colonP = name.find(':');
|
||||
if (colonP != name.npos)
|
||||
{
|
||||
// used to upload files (save data)
|
||||
data << "content-disposition: form-data; name=\"" << name.substr(0, colonP) << "\"";
|
||||
data << "filename=\"" << name.substr(colonP+1) << "\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
data << "content-disposition: form-data; name=\"" << name << "\"";
|
||||
}
|
||||
data << "\r\n\r\n";
|
||||
data << value;
|
||||
data << "\r\n";
|
||||
}
|
||||
data << "--" << boundary << "--\r\n";
|
||||
return data.str();
|
||||
}
|
||||
|
||||
// add the header needed to make POSTS work
|
||||
void http_add_multipart_header(void *ctx, std::string boundary)
|
||||
{
|
||||
std::string header = "multipart/form-data, boundary=" + boundary;
|
||||
http_async_add_header(ctx, "Content-type", header.c_str());
|
||||
}
|
||||
|
||||
char *http_multipart_post(const char *uri, const char *const *names, const char *const *parts, size_t *plens, const char *user, const char *pass, const char *session_id, int *ret, int *len)
|
||||
{
|
||||
void *ctx;
|
||||
|
@ -20,7 +20,11 @@
|
||||
#ifndef HTTP_H
|
||||
#define HTTP_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
static const char hexChars[] = "0123456789abcdef";
|
||||
static long http_timeout = 15;
|
||||
|
||||
void http_init(char *proxy);
|
||||
void http_done(void);
|
||||
@ -37,7 +41,11 @@ int http_async_req_status(void *ctx);
|
||||
void http_async_get_length(void *ctx, int *total, int *done);
|
||||
char *http_async_req_stop(void *ctx, int *ret, int *len);
|
||||
void http_async_req_close(void *ctx);
|
||||
void http_force_close(void *ctx);
|
||||
|
||||
std::string FindBoundary(std::map<std::string, std::string>, std::string boundary);
|
||||
std::string GetMultipartMessage(std::map<std::string, std::string>, std::string boundary);
|
||||
void http_add_multipart_header(void *ctx, std::string boundary);
|
||||
char *http_multipart_post(const char *uri, const char *const *names, const char *const *parts, size_t *plens, const char *user, const char *pass, const char * session_id, int *ret, int *len);
|
||||
void *http_multipart_post_async(const char *uri, const char *const *names, const char *const *parts, int *plens, const char *user, const char *pass, const char *session_id);
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "common/tpt-thread.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "Singleton.h"
|
||||
#include "common/Singleton.h"
|
||||
|
||||
class GameSave;
|
||||
class VideoBuffer;
|
||||
|
@ -68,11 +68,11 @@ public:
|
||||
SaveOpenCallback(GameController * cc_) { cc = cc_; }
|
||||
virtual void ControllerExit()
|
||||
{
|
||||
if(cc->activePreview->GetDoOpen() && cc->activePreview->GetSave())
|
||||
if(cc->activePreview->GetDoOpen() && cc->activePreview->GetSaveInfo())
|
||||
{
|
||||
try
|
||||
{
|
||||
cc->LoadSave(cc->activePreview->GetSave());
|
||||
cc->LoadSave(cc->activePreview->GetSaveInfo());
|
||||
}
|
||||
catch(GameModelException & ex)
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stack>
|
||||
#include "Singleton.h"
|
||||
#include "common/Singleton.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "Window.h"
|
||||
|
||||
|
@ -58,12 +58,13 @@ PreviewController::PreviewController(int saveID, bool instant, ControllerCallbac
|
||||
|
||||
void PreviewController::Update()
|
||||
{
|
||||
if(loginWindow && loginWindow->HasExited == true)
|
||||
previewModel->Update();
|
||||
if (loginWindow && loginWindow->HasExited == true)
|
||||
{
|
||||
delete loginWindow;
|
||||
loginWindow = NULL;
|
||||
}
|
||||
if(previewModel->GetDoOpen() && previewModel->GetSave() && previewModel->GetSave()->GetGameSave())
|
||||
if (previewModel->GetDoOpen() && previewModel->GetSaveInfo() && previewModel->GetSaveInfo()->GetGameSave())
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
@ -104,9 +105,9 @@ void PreviewController::NotifyAuthUserChanged(Client * sender)
|
||||
previewModel->SetCommentBoxEnabled(sender->GetAuthUser().ID);
|
||||
}
|
||||
|
||||
SaveInfo * PreviewController::GetSave()
|
||||
SaveInfo * PreviewController::GetSaveInfo()
|
||||
{
|
||||
return previewModel->GetSave();
|
||||
return previewModel->GetSaveInfo();
|
||||
}
|
||||
|
||||
bool PreviewController::GetDoOpen()
|
||||
@ -132,11 +133,11 @@ void PreviewController::Report(std::string message)
|
||||
|
||||
void PreviewController::FavouriteSave()
|
||||
{
|
||||
if(previewModel->GetSave() && Client::Ref().GetAuthUser().ID)
|
||||
if(previewModel->GetSaveInfo() && Client::Ref().GetAuthUser().ID)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(previewModel->GetSave()->Favourite)
|
||||
if(previewModel->GetSaveInfo()->Favourite)
|
||||
previewModel->SetFavourite(false);
|
||||
else
|
||||
previewModel->SetFavourite(true);
|
||||
|
@ -30,7 +30,7 @@ public:
|
||||
void Report(std::string message);
|
||||
void ShowLogin();
|
||||
bool GetDoOpen();
|
||||
SaveInfo * GetSave();
|
||||
SaveInfo * GetSaveInfo();
|
||||
PreviewView * GetView() { return previewView; }
|
||||
void Update();
|
||||
void FavouriteSave();
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <cmath>
|
||||
#include "PreviewModel.h"
|
||||
#include "Format.h"
|
||||
#include "client/Client.h"
|
||||
#include "client/GameSave.h"
|
||||
#include "gui/dialogues/ErrorMessage.h"
|
||||
@ -8,7 +9,7 @@
|
||||
PreviewModel::PreviewModel():
|
||||
doOpen(false),
|
||||
canOpen(true),
|
||||
save(NULL),
|
||||
saveInfo(NULL),
|
||||
saveData(NULL),
|
||||
saveComments(NULL),
|
||||
commentBoxEnabled(false),
|
||||
@ -21,10 +22,10 @@ PreviewModel::PreviewModel():
|
||||
|
||||
void PreviewModel::SetFavourite(bool favourite)
|
||||
{
|
||||
if(save)
|
||||
if (saveInfo)
|
||||
{
|
||||
if (Client::Ref().FavouriteSave(save->id, favourite) == RequestOkay)
|
||||
save->Favourite = favourite;
|
||||
if (Client::Ref().FavouriteSave(saveInfo->id, favourite) == RequestOkay)
|
||||
saveInfo->Favourite = favourite;
|
||||
else if (favourite)
|
||||
throw PreviewModelException("Error, could not fav. the save: " + Client::Ref().GetLastError());
|
||||
else
|
||||
@ -49,37 +50,48 @@ void PreviewModel::SetCommentBoxEnabled(bool enabledState)
|
||||
|
||||
void PreviewModel::UpdateSave(int saveID, int saveDate)
|
||||
{
|
||||
this->tSaveID = saveID;
|
||||
this->tSaveDate = saveDate;
|
||||
this->saveID = saveID;
|
||||
this->saveDate = saveDate;
|
||||
|
||||
if (save)
|
||||
if (saveInfo)
|
||||
{
|
||||
delete save;
|
||||
save = NULL;
|
||||
delete saveInfo;
|
||||
saveInfo = NULL;
|
||||
}
|
||||
if (saveData)
|
||||
{
|
||||
delete saveData;
|
||||
saveData = NULL;
|
||||
}
|
||||
if (saveComments)
|
||||
{
|
||||
for (size_t i = 0; i < saveComments->size(); i++)
|
||||
delete saveComments->at(i);
|
||||
saveComments->clear();
|
||||
delete saveComments;
|
||||
saveComments = NULL;
|
||||
}
|
||||
ClearComments();
|
||||
notifySaveChanged();
|
||||
notifySaveCommentsChanged();
|
||||
|
||||
RequestBroker::Ref().Start(Client::Ref().GetSaveDataAsync(saveID, saveDate), this, 1);
|
||||
RequestBroker::Ref().Start(Client::Ref().GetSaveAsync(saveID, saveDate), this, 2);
|
||||
std::stringstream urlStream;
|
||||
if (saveDate)
|
||||
urlStream << "http://" << STATICSERVER << "/" << saveID << "_" << saveDate << ".cps";
|
||||
else
|
||||
urlStream << "http://" << STATICSERVER << "/" << saveID << ".cps";
|
||||
saveDataDownload = new Download(urlStream.str());
|
||||
saveDataDownload->Start();
|
||||
|
||||
urlStream.str("");
|
||||
urlStream << "http://" << SERVER << "/Browse/View.json?ID=" << saveID;
|
||||
if (saveDate)
|
||||
urlStream << "&Date=" << saveDate;
|
||||
saveInfoDownload = new Download(urlStream.str());
|
||||
saveInfoDownload->AuthHeaders(format::NumberToString(Client::Ref().GetAuthUser().ID), Client::Ref().GetAuthUser().SessionID);
|
||||
saveInfoDownload->Start();
|
||||
|
||||
if (!GetDoOpen())
|
||||
{
|
||||
commentsLoaded = false;
|
||||
RequestBroker::Ref().Start(Client::Ref().GetCommentsAsync(saveID, (commentsPageNumber-1)*20, 20), this, 3);
|
||||
|
||||
urlStream.str("");
|
||||
urlStream << "http://" << SERVER << "/Browse/Comments.json?ID=" << saveID << "&Start=" << (commentsPageNumber-1)*20 << "&Count=20";
|
||||
commentsDownload = new Download(urlStream.str());
|
||||
commentsDownload->AuthHeaders(format::NumberToString(Client::Ref().GetAuthUser().ID), Client::Ref().GetAuthUser().SessionID);
|
||||
commentsDownload->Start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,9 +110,9 @@ bool PreviewModel::GetCanOpen()
|
||||
return canOpen;
|
||||
}
|
||||
|
||||
SaveInfo * PreviewModel::GetSave()
|
||||
SaveInfo * PreviewModel::GetSaveInfo()
|
||||
{
|
||||
return save;
|
||||
return saveInfo;
|
||||
}
|
||||
|
||||
int PreviewModel::GetCommentsPageNum()
|
||||
@ -123,18 +135,17 @@ void PreviewModel::UpdateComments(int pageNumber)
|
||||
if (commentsLoaded)
|
||||
{
|
||||
commentsLoaded = false;
|
||||
if (saveComments)
|
||||
{
|
||||
for (size_t i = 0; i < saveComments->size(); i++)
|
||||
delete saveComments->at(i);
|
||||
saveComments->clear();
|
||||
delete saveComments;
|
||||
saveComments = NULL;
|
||||
}
|
||||
ClearComments();
|
||||
|
||||
commentsPageNumber = pageNumber;
|
||||
if (!GetDoOpen())
|
||||
RequestBroker::Ref().Start(Client::Ref().GetCommentsAsync(tSaveID, (commentsPageNumber-1)*20, 20), this, 3);
|
||||
{
|
||||
std::stringstream urlStream;
|
||||
urlStream << "http://" << SERVER << "/Browse/Comments.json?ID=" << saveID << "&Start=" << (commentsPageNumber-1)*20 << "&Count=20";
|
||||
commentsDownload = new Download(urlStream.str());
|
||||
commentsDownload->AuthHeaders(format::NumberToString(Client::Ref().GetAuthUser().ID).c_str(), Client::Ref().GetAuthUser().SessionID.c_str());
|
||||
commentsDownload->Start();
|
||||
}
|
||||
|
||||
notifySaveCommentsChanged();
|
||||
notifyCommentsPageChanged();
|
||||
@ -143,93 +154,186 @@ void PreviewModel::UpdateComments(int pageNumber)
|
||||
|
||||
void PreviewModel::CommentAdded()
|
||||
{
|
||||
if (save)
|
||||
save->Comments++;
|
||||
if (saveInfo)
|
||||
saveInfo->Comments++;
|
||||
commentsTotal++;
|
||||
}
|
||||
|
||||
void PreviewModel::OnResponseReady(void * object, int identifier)
|
||||
void PreviewModel::OnSaveReady()
|
||||
{
|
||||
if (identifier == 1)
|
||||
commentsTotal = saveInfo->Comments;
|
||||
try
|
||||
{
|
||||
delete saveData;
|
||||
saveData = (std::vector<unsigned char>*)object;
|
||||
GameSave *gameSave = new GameSave(*saveData);
|
||||
if (gameSave->fromNewerVersion)
|
||||
new ErrorMessage("This save is from a newer version", "Please update TPT in game or at http://powdertoy.co.uk");
|
||||
saveInfo->SetGameSave(gameSave);
|
||||
}
|
||||
if (identifier == 2)
|
||||
catch(ParseException &e)
|
||||
{
|
||||
delete save;
|
||||
save = (SaveInfo*)object;
|
||||
new ErrorMessage("Error", e.what());
|
||||
canOpen = false;
|
||||
}
|
||||
if (identifier == 3)
|
||||
{
|
||||
if (saveComments)
|
||||
{
|
||||
for (size_t i = 0; i < saveComments->size(); i++)
|
||||
delete saveComments->at(i);
|
||||
saveComments->clear();
|
||||
delete saveComments;
|
||||
saveComments = NULL;
|
||||
}
|
||||
saveComments = (std::vector<SaveComment*>*)object;
|
||||
commentsLoaded = true;
|
||||
notifySaveChanged();
|
||||
notifyCommentsPageChanged();
|
||||
//make sure author name comments are red
|
||||
if (commentsLoaded)
|
||||
notifySaveCommentsChanged();
|
||||
notifyCommentsPageChanged();
|
||||
}
|
||||
}
|
||||
|
||||
if (identifier == 1 || identifier == 2)
|
||||
void PreviewModel::ClearComments()
|
||||
{
|
||||
if (saveComments)
|
||||
{
|
||||
if (save && saveData)
|
||||
{
|
||||
commentsTotal = save->Comments;
|
||||
try
|
||||
{
|
||||
GameSave *gameSave = new GameSave(*saveData);
|
||||
if (gameSave->fromNewerVersion)
|
||||
new ErrorMessage("This save is from a newer version", "Please update TPT in game or at http://powdertoy.co.uk");
|
||||
save->SetGameSave(gameSave);
|
||||
}
|
||||
catch(ParseException &e)
|
||||
{
|
||||
new ErrorMessage("Error", e.what());
|
||||
canOpen = false;
|
||||
}
|
||||
notifySaveChanged();
|
||||
notifyCommentsPageChanged();
|
||||
//make sure author name comments are red
|
||||
if (commentsLoaded)
|
||||
notifySaveCommentsChanged();
|
||||
}
|
||||
for (size_t i = 0; i < saveComments->size(); i++)
|
||||
delete saveComments->at(i);
|
||||
saveComments->clear();
|
||||
delete saveComments;
|
||||
saveComments = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewModel::OnResponseFailed(int identifier)
|
||||
bool PreviewModel::ParseSaveInfo(char * saveInfoResponse)
|
||||
{
|
||||
if (identifier == 3)
|
||||
delete saveInfo;
|
||||
|
||||
try
|
||||
{
|
||||
if (saveComments)
|
||||
{
|
||||
for (size_t i = 0; i < saveComments->size(); i++)
|
||||
delete saveComments->at(i);
|
||||
saveComments->clear();
|
||||
delete saveComments;
|
||||
saveComments = NULL;
|
||||
}
|
||||
saveComments = NULL;
|
||||
commentsLoaded = true;
|
||||
notifySaveCommentsChanged();
|
||||
std::istringstream dataStream(saveInfoResponse);
|
||||
Json::Value objDocument;
|
||||
dataStream >> objDocument;
|
||||
|
||||
int tempID = objDocument["ID"].asInt();
|
||||
int tempScoreUp = objDocument["ScoreUp"].asInt();
|
||||
int tempScoreDown = objDocument["ScoreDown"].asInt();
|
||||
int tempMyScore = objDocument["ScoreMine"].asInt();
|
||||
std::string tempUsername = objDocument["Username"].asString();
|
||||
std::string tempName = objDocument["Name"].asString();
|
||||
std::string tempDescription = objDocument["Description"].asString();
|
||||
int tempDate = objDocument["Date"].asInt();
|
||||
bool tempPublished = objDocument["Published"].asBool();
|
||||
bool tempFavourite = objDocument["Favourite"].asBool();
|
||||
int tempComments = objDocument["Comments"].asInt();
|
||||
int tempViews = objDocument["Views"].asInt();
|
||||
int tempVersion = objDocument["Version"].asInt();
|
||||
|
||||
Json::Value tagsArray = objDocument["Tags"];
|
||||
std::list<std::string> tempTags;
|
||||
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
||||
tempTags.push_back(tagsArray[j].asString());
|
||||
|
||||
saveInfo = new SaveInfo(tempID, tempDate, tempScoreUp, tempScoreDown,
|
||||
tempMyScore, tempUsername, tempName, tempDescription,
|
||||
tempPublished, tempTags);
|
||||
saveInfo->Comments = tempComments;
|
||||
saveInfo->Favourite = tempFavourite;
|
||||
saveInfo->Views = tempViews;
|
||||
saveInfo->Version = tempVersion;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
catch (std::exception &e)
|
||||
{
|
||||
for (size_t i = 0; i < observers.size(); i++)
|
||||
saveInfo = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PreviewModel::ParseComments(char *commentsResponse)
|
||||
{
|
||||
ClearComments();
|
||||
saveComments = new std::vector<SaveComment*>();
|
||||
try
|
||||
{
|
||||
std::istringstream dataStream(commentsResponse);
|
||||
Json::Value commentsArray;
|
||||
dataStream >> commentsArray;
|
||||
|
||||
for (Json::UInt j = 0; j < commentsArray.size(); j++)
|
||||
{
|
||||
observers[i]->SaveLoadingError(Client::Ref().GetLastError());
|
||||
int userID = format::StringToNumber<int>(commentsArray[j]["UserID"].asString());
|
||||
std::string username = commentsArray[j]["Username"].asString();
|
||||
std::string formattedUsername = commentsArray[j]["FormattedUsername"].asString();
|
||||
if (formattedUsername == "jacobot")
|
||||
formattedUsername = "\bt" + formattedUsername;
|
||||
std::string comment = commentsArray[j]["Text"].asString();
|
||||
saveComments->push_back(new SaveComment(userID, username, formattedUsername, comment));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewModel::Update()
|
||||
{
|
||||
if (saveDataDownload && saveDataDownload->CheckDone())
|
||||
{
|
||||
int status, length;
|
||||
char *ret = saveDataDownload->Finish(&length, &status);
|
||||
|
||||
Client::Ref().ParseServerReturn(NULL, status, true);
|
||||
if (status == 200 && ret)
|
||||
{
|
||||
delete saveData;
|
||||
saveData = new std::vector<unsigned char>(ret, ret+length);
|
||||
if (saveInfo && saveData)
|
||||
OnSaveReady();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < observers.size(); i++)
|
||||
{
|
||||
observers[i]->SaveLoadingError(Client::Ref().GetLastError());
|
||||
}
|
||||
}
|
||||
saveDataDownload = NULL;
|
||||
}
|
||||
|
||||
if (saveInfoDownload && saveInfoDownload->CheckDone())
|
||||
{
|
||||
int status;
|
||||
char *ret = saveInfoDownload->Finish(NULL, &status);
|
||||
|
||||
Client::Ref().ParseServerReturn(NULL, status, true);
|
||||
if (status == 200 && ret)
|
||||
{
|
||||
if (ParseSaveInfo(ret))
|
||||
{
|
||||
if (saveInfo && saveData)
|
||||
OnSaveReady();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < observers.size(); i++)
|
||||
observers[i]->SaveLoadingError("Could not parse save info");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < observers.size(); i++)
|
||||
observers[i]->SaveLoadingError(Client::Ref().GetLastError());
|
||||
}
|
||||
saveInfoDownload = NULL;
|
||||
}
|
||||
|
||||
if (commentsDownload && commentsDownload->CheckDone())
|
||||
{
|
||||
int status;
|
||||
char *ret = commentsDownload->Finish(NULL, &status);
|
||||
ClearComments();
|
||||
|
||||
Client::Ref().ParseServerReturn(NULL, status, true);
|
||||
if (status == 200 && ret)
|
||||
ParseComments(ret);
|
||||
|
||||
commentsLoaded = true;
|
||||
notifySaveCommentsChanged();
|
||||
notifyCommentsPageChanged();
|
||||
|
||||
commentsDownload = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SaveComment*> * PreviewModel::GetComments()
|
||||
@ -281,14 +385,13 @@ void PreviewModel::AddObserver(PreviewView * observer)
|
||||
|
||||
PreviewModel::~PreviewModel()
|
||||
{
|
||||
RequestBroker::Ref().DetachRequestListener(this);
|
||||
delete save;
|
||||
if (saveDataDownload)
|
||||
saveDataDownload->Cancel();
|
||||
if (saveInfoDownload)
|
||||
saveInfoDownload->Cancel();
|
||||
if (commentsDownload)
|
||||
commentsDownload->Cancel();
|
||||
delete saveInfo;
|
||||
delete saveData;
|
||||
if (saveComments)
|
||||
{
|
||||
for (size_t i = 0; i < saveComments->size(); i++)
|
||||
delete saveComments->at(i);
|
||||
saveComments->clear();
|
||||
delete saveComments;
|
||||
}
|
||||
ClearComments();
|
||||
}
|
||||
|
@ -3,21 +3,20 @@
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "common/tpt-thread.h"
|
||||
#include "PreviewView.h"
|
||||
#include "client/SaveInfo.h"
|
||||
#include "gui/preview/Comment.h"
|
||||
#include "gui/search/Thumbnail.h"
|
||||
#include "client/requestbroker/RequestListener.h"
|
||||
#include "client/Download.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class PreviewView;
|
||||
class PreviewModel: RequestListener {
|
||||
class PreviewModel {
|
||||
bool doOpen;
|
||||
bool canOpen;
|
||||
vector<PreviewView*> observers;
|
||||
SaveInfo * save;
|
||||
SaveInfo * saveInfo;
|
||||
std::vector<unsigned char> * saveData;
|
||||
std::vector<SaveComment*> * saveComments;
|
||||
void notifySaveChanged();
|
||||
@ -25,11 +24,12 @@ class PreviewModel: RequestListener {
|
||||
void notifyCommentsPageChanged();
|
||||
void notifyCommentBoxEnabledChanged();
|
||||
|
||||
//Background retrieval
|
||||
int tSaveID;
|
||||
int tSaveDate;
|
||||
Download * saveDataDownload;
|
||||
Download * saveInfoDownload;
|
||||
Download * commentsDownload;
|
||||
int saveID;
|
||||
int saveDate;
|
||||
|
||||
//
|
||||
bool commentBoxEnabled;
|
||||
bool commentsLoaded;
|
||||
int commentsTotal;
|
||||
@ -37,7 +37,7 @@ class PreviewModel: RequestListener {
|
||||
|
||||
public:
|
||||
PreviewModel();
|
||||
SaveInfo * GetSave();
|
||||
SaveInfo * GetSaveInfo();
|
||||
std::vector<SaveComment*> * GetComments();
|
||||
|
||||
bool GetCommentBoxEnabled();
|
||||
@ -56,8 +56,10 @@ public:
|
||||
bool GetCanOpen();
|
||||
void SetDoOpen(bool doOpen);
|
||||
void Update();
|
||||
virtual void OnResponseReady(void * object, int identifier);
|
||||
virtual void OnResponseFailed(int identifier);
|
||||
void ClearComments();
|
||||
void OnSaveReady();
|
||||
bool ParseSaveInfo(char * saveInfoResponse);
|
||||
bool ParseComments(char * commentsResponse);
|
||||
virtual ~PreviewModel();
|
||||
};
|
||||
|
||||
|
@ -415,7 +415,7 @@ void PreviewView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, b
|
||||
|
||||
void PreviewView::NotifySaveChanged(PreviewModel * sender)
|
||||
{
|
||||
SaveInfo * save = sender->GetSave();
|
||||
SaveInfo * save = sender->GetSaveInfo();
|
||||
delete savePreview;
|
||||
savePreview = NULL;
|
||||
if(save)
|
||||
@ -598,7 +598,7 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
|
||||
tempUsername->Appearance.VerticalAlign = ui::Appearance::AlignBottom;
|
||||
if (Client::Ref().GetAuthUser().ID && Client::Ref().GetAuthUser().Username == comments->at(i)->authorName)
|
||||
tempUsername->SetTextColour(ui::Colour(255, 255, 100));
|
||||
else if (sender->GetSave() && sender->GetSave()->GetUserName() == comments->at(i)->authorName)
|
||||
else if (sender->GetSaveInfo() && sender->GetSaveInfo()->GetUserName() == comments->at(i)->authorName)
|
||||
tempUsername->SetTextColour(ui::Colour(255, 100, 100));
|
||||
currentY += 16;
|
||||
|
||||
|
@ -19,9 +19,9 @@ public:
|
||||
OpenCallback(SearchController * cc_) { cc = cc_; }
|
||||
virtual void ControllerExit()
|
||||
{
|
||||
if(cc->activePreview->GetDoOpen() && cc->activePreview->GetSave())
|
||||
if(cc->activePreview->GetDoOpen() && cc->activePreview->GetSaveInfo())
|
||||
{
|
||||
cc->searchModel->SetLoadedSave(cc->activePreview->GetSave());
|
||||
cc->searchModel->SetLoadedSave(cc->activePreview->GetSaveInfo());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3,7 +3,7 @@
|
||||
#ifdef OGLI
|
||||
#include "graphics/OpenGLHeaders.h"
|
||||
#endif
|
||||
#include "Singleton.h"
|
||||
#include "common/Singleton.h"
|
||||
|
||||
class GameSave;
|
||||
class VideoBuffer;
|
||||
|
Loading…
Reference in New Issue
Block a user