Make thumbnailbroker more general purpose, image requests for requestbroker, avatars in previewview

This commit is contained in:
Simon Robertshaw
2013-03-12 21:17:19 +00:00
parent e6bca489c9
commit 86b7a11800
14 changed files with 416 additions and 342 deletions

View File

@@ -42,7 +42,7 @@ public:
UpdateInfo(int time, std::string file, BuildType type) : Major(0), Minor(0), Build(0), Time(time), File(file), Type(type) {} UpdateInfo(int time, std::string file, BuildType type) : Major(0), Minor(0), Build(0), Time(time), File(file), Type(type) {}
}; };
class ThumbnailListener; class RequestListener;
class ClientListener; class ClientListener;
class Client: public Singleton<Client> { class Client: public Singleton<Client> {
private: private:

View File

@@ -3,7 +3,7 @@
#include <typeinfo> #include <typeinfo>
#include <time.h> #include <time.h>
#include "RequestBroker.h" #include "RequestBroker.h"
#include "ThumbnailListener.h" #include "RequestListener.h"
#include "Client.h" #include "Client.h"
#include "HTTP.h" #include "HTTP.h"
#include "GameSave.h" #include "GameSave.h"
@@ -15,14 +15,18 @@
RequestBroker::RequestBroker() RequestBroker::RequestBroker()
{ {
thumbnailQueueRunning = false; thumbnailQueueRunning = false;
//thumbnailQueueMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init (&thumbnailQueueMutex, NULL);
//listenersMutex = PTHREAD_MUTEX_INITIALIZER; //listenersMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init (&listenersMutex, NULL); pthread_mutex_init (&listenersMutex, NULL);
pthread_mutex_init (&runningMutex, NULL); pthread_mutex_init (&runningMutex, NULL);
pthread_mutex_init (&requestQueueMutex, NULL);
pthread_mutex_init (&completeQueueMutex, NULL);
} }
RequestBroker::~RequestBroker() RequestBroker::~RequestBroker()
@@ -58,38 +62,63 @@ void RequestBroker::Shutdown()
else else
pthread_mutex_unlock(&runningMutex); pthread_mutex_unlock(&runningMutex);
std::vector<Request*>::iterator req = activeRequests.begin();
for (std::list<ThumbnailRequest>::iterator iter = currentRequests.begin(), end = currentRequests.end(); iter != end; ++iter) while(req != activeRequests.end())
{ {
ThumbnailRequest req = *iter; (*req)->Cleanup();
if(req.HTTPContext) delete (*req);
{ req++;
http_async_req_close(req.HTTPContext);
}
} }
} }
void RequestBroker::RenderThumbnail(GameSave * gameSave, int width, int height, ThumbnailListener * tListener) void RequestBroker::RenderThumbnail(GameSave * gameSave, int width, int height, RequestListener * tListener)
{ {
RenderThumbnail(gameSave, true, true, width, height, tListener); RenderThumbnail(gameSave, true, true, width, height, tListener);
} }
void RequestBroker::RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, ThumbnailListener * tListener) void RequestBroker::RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, RequestListener * tListener)
{ {
AttachThumbnailListener(tListener); ListenerHandle handle = AttachRequestListener(tListener);
pthread_mutex_lock(&thumbnailQueueMutex);
renderRequests.push_back(ThumbRenderRequest(new GameSave(*gameSave), decorations, fire, width, height, ListenerHandle(tListener->ListenerRand, tListener))); ThumbRenderRequest * r = new ThumbRenderRequest(new GameSave(*gameSave), decorations, fire, width, height, handle);
pthread_mutex_unlock(&thumbnailQueueMutex);
pthread_mutex_lock(&requestQueueMutex);
requestQueue.push_back(r);
pthread_mutex_unlock(&requestQueueMutex);
assureRunning(); assureRunning();
} }
void RequestBroker::RetrieveThumbnail(int saveID, int saveDate, int width, int height, ThumbnailListener * tListener) void RequestBroker::RetrieveThumbnail(int saveID, int saveDate, int width, int height, RequestListener * tListener)
{ {
AttachThumbnailListener(tListener); std::stringstream urlStream;
pthread_mutex_lock(&thumbnailQueueMutex); urlStream << "http://" << STATICSERVER << "/" << saveID;
thumbnailRequests.push_back(ThumbnailRequest(saveID, saveDate, width, height, ListenerHandle(tListener->ListenerRand, tListener))); if(saveDate)
pthread_mutex_unlock(&thumbnailQueueMutex); {
urlStream << "_" << saveDate;
}
urlStream << "_small.pti";
RetrieveImage(urlStream.str(), width, height, tListener);
}
void RequestBroker::RetrieveAvatar(std::string username, int width, int height, RequestListener * tListener)
{
std::stringstream urlStream;
urlStream << "http://" << STATICSERVER << "/avatars/" << username << ".pti";
RetrieveImage(urlStream.str(), width, height, tListener);
}
void RequestBroker::RetrieveImage(std::string imageUrl, int width, int height, RequestListener * tListener)
{
ListenerHandle handle = AttachRequestListener(tListener);
ImageRequest * r = new ImageRequest(imageUrl, width, height, handle);
pthread_mutex_lock(&requestQueueMutex);
requestQueue.push_back(r);
pthread_mutex_unlock(&requestQueueMutex);
assureRunning(); assureRunning();
} }
@@ -102,23 +131,24 @@ void * RequestBroker::thumbnailQueueProcessHelper(void * ref)
void RequestBroker::FlushThumbQueue() void RequestBroker::FlushThumbQueue()
{ {
pthread_mutex_lock(&thumbnailQueueMutex); pthread_mutex_lock(&completeQueueMutex);
while(thumbnailComplete.size()) while(completeQueue.size())
{ {
if(CheckThumbnailListener(thumbnailComplete.front().first)) if(CheckRequestListener(completeQueue.front()->Listener))
{ {
thumbnailComplete.front().first.second->OnThumbnailReady(thumbnailComplete.front().second); completeQueue.front()->Listener.second->OnResponseReady(completeQueue.front()->ResultObject);
} }
else else
{ {
#ifdef DEBUG #ifdef DEBUG
std::cout << typeid(*this).name() << " Listener lost, discarding request" << std::endl; std::cout << typeid(*this).name() << " Listener lost, discarding request" << std::endl;
#endif #endif
delete thumbnailComplete.front().second; completeQueue.front()->Cleanup();
} }
thumbnailComplete.pop_front(); delete completeQueue.front();
completeQueue.pop();
} }
pthread_mutex_unlock(&thumbnailQueueMutex); pthread_mutex_unlock(&completeQueueMutex);
} }
void RequestBroker::thumbnailQueueProcessTH() void RequestBroker::thumbnailQueueProcessTH()
@@ -150,216 +180,211 @@ void RequestBroker::thumbnailQueueProcessTH()
break; break;
} }
if(activeRequests.size())
//Renderer
pthread_mutex_lock(&thumbnailQueueMutex);
if(renderRequests.size())
{ {
std::vector<Request*>::iterator req = activeRequests.begin();
while(req != activeRequests.end())
{
ProcessResponse resultStatus = OK;
Request * r = *req;
switch(r->Type)
{
case Request::ThumbnailRender:
resultStatus = processThumbnailRender(*(ThumbRenderRequest*)r);
break;
case Request::Image:
resultStatus = processImage(*(ImageRequest*)r);
break;
}
if(resultStatus == Duplicate || resultStatus == Failed || resultStatus == Finished)
{
req = activeRequests.erase(req);
}
else
{
req++;
}
}
lastAction = time(NULL); lastAction = time(NULL);
ThumbRenderRequest req;
req = renderRequests.front();
renderRequests.pop_front();
pthread_mutex_unlock(&thumbnailQueueMutex);
#ifdef DEBUG
std::cout << typeid(*this).name() << " Processing render request" << std::endl;
#endif
Thumbnail * thumbnail = SaveRenderer::Ref().Render(req.Save, req.Decorations, req.Fire);
delete req.Save;
if(thumbnail)
{
thumbnail->Resize(req.Width, req.Height);
pthread_mutex_lock(&thumbnailQueueMutex);
thumbnailComplete.push_back(std::pair<ListenerHandle, Thumbnail*>(req.CompletedListener, thumbnail));
pthread_mutex_unlock(&thumbnailQueueMutex);
} }
//Move any items from the request queue to the processing queue
pthread_mutex_lock(&requestQueueMutex);
std::vector<Request*>::iterator newReq = requestQueue.begin();
while(newReq != requestQueue.end())
{
if(activeRequests.size() > 5)
{
break;
} }
else else
{ {
pthread_mutex_unlock(&thumbnailQueueMutex); activeRequests.push_back(*newReq);
} newReq = requestQueue.erase(newReq);
//Renderer
pthread_mutex_lock(&thumbnailQueueMutex);
if(thumbnailRequests.size())
{
lastAction = time(NULL);
Thumbnail * thumbnail = NULL;
ThumbnailRequest req;
req = thumbnailRequests.front();
//Check the cache
for(std::deque<std::pair<ThumbnailID, Thumbnail*> >::iterator iter = thumbnailCache.begin(), end = thumbnailCache.end(); iter != end; ++iter)
{
if((*iter).first == req.ID)
{
thumbnail = (*iter).second;
#ifdef DEBUG
std::cout << typeid(*this).name() << " " << req.ID.SaveID << ":" << req.ID.SaveDate << " found in cache" << std::endl;
#endif
} }
} }
pthread_mutex_unlock(&requestQueueMutex);
if(thumbnail)
{
//Got thumbnail from cache
thumbnailRequests.pop_front();
pthread_mutex_unlock(&thumbnailQueueMutex);
for(std::vector<ThumbnailSpec>::iterator specIter = req.SubRequests.begin(), specEnd = req.SubRequests.end(); specIter != specEnd; ++specIter)
{
Thumbnail * tempThumbnail = new Thumbnail(*thumbnail);
tempThumbnail->Resize((*specIter).Width, (*specIter).Height);
pthread_mutex_lock(&thumbnailQueueMutex);
thumbnailComplete.push_back(std::pair<ListenerHandle, Thumbnail*>((*specIter).CompletedListener, tempThumbnail));
pthread_mutex_unlock(&thumbnailQueueMutex);
}
}
else
{
//Check for ongoing requests
bool requested = false;
for(std::list<ThumbnailRequest>::iterator iter = currentRequests.begin(), end = currentRequests.end(); iter != end; ++iter)
{
if((*iter).ID == req.ID)
{
requested = true;
#ifdef DEBUG
std::cout << typeid(*this).name() << " Request for " << req.ID.SaveID << ":" << req.ID.SaveDate << " found, appending." << std::endl;
#endif
//Add the current listener to the item already being requested
(*iter).SubRequests.push_back(req.SubRequests.front());
}
}
if(requested)
{
//Already requested
thumbnailRequests.pop_front();
pthread_mutex_unlock(&thumbnailQueueMutex);
}
else if(currentRequests.size() < IMGCONNS) //If it's not already being requested and we still have more space for a new connection, request it
{
thumbnailRequests.pop_front();
pthread_mutex_unlock(&thumbnailQueueMutex);
//If it's not already being requested, request it
std::stringstream urlStream;
urlStream << "http://" << STATICSERVER << "/" << req.ID.SaveID;
if(req.ID.SaveDate)
{
urlStream << "_" << req.ID.SaveDate;
}
urlStream << "_small.pti";
#ifdef DEBUG
std::cout << typeid(*this).name() << " Creating new request for " << req.ID.SaveID << ":" << req.ID.SaveDate << std::endl;
#endif
req.HTTPContext = http_async_req_start(NULL, (char *)urlStream.str().c_str(), NULL, 0, 0);
req.RequestTime = time(NULL);
currentRequests.push_back(req);
}
else
{
//Already full of requests
pthread_mutex_unlock(&thumbnailQueueMutex);
}
}
}
else
{
pthread_mutex_unlock(&thumbnailQueueMutex);
}
std::list<ThumbnailRequest>::iterator iter = currentRequests.begin();
while (iter != currentRequests.end())
{
lastAction = time(NULL);
ThumbnailRequest req = *iter;
Thumbnail * thumbnail = NULL;
if(http_async_req_status(req.HTTPContext))
{
pixel * thumbData;
char * data;
int status, data_size, imgw, imgh;
data = http_async_req_stop(req.HTTPContext, &status, &data_size);
if (status == 200 && data)
{
thumbData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh);
free(data);
if(thumbData)
{
thumbnail = new Thumbnail(req.ID.SaveID, req.ID.SaveID, thumbData, ui::Point(imgw, imgh));
free(thumbData);
}
else
{
//Error thumbnail
VideoBuffer errorThumb(128, 128);
errorThumb.SetCharacter(64, 64, 'x', 255, 255, 255, 255);
thumbnail = new Thumbnail(req.ID.SaveID, req.ID.SaveID, errorThumb.Buffer, ui::Point(errorThumb.Width, errorThumb.Height));
}
if(thumbnailCache.size() >= THUMB_CACHE_SIZE)
{
delete thumbnailCache.front().second;
thumbnailCache.pop_front();
}
thumbnailCache.push_back(std::pair<ThumbnailID, Thumbnail*>(req.ID, thumbnail));
for(std::vector<ThumbnailSpec>::iterator specIter = req.SubRequests.begin(), specEnd = req.SubRequests.end(); specIter != specEnd; ++specIter)
{
Thumbnail * tempThumbnail = new Thumbnail(*thumbnail);
tempThumbnail->Resize((*specIter).Width, (*specIter).Height);
pthread_mutex_lock(&thumbnailQueueMutex);
thumbnailComplete.push_back(std::pair<ListenerHandle, Thumbnail*>((*specIter).CompletedListener, tempThumbnail));
pthread_mutex_unlock(&thumbnailQueueMutex);
}
}
else
{
#ifdef DEBUG
std::cout << typeid(*this).name() << " Request for " << req.ID.SaveID << ":" << req.ID.SaveDate << " failed with status " << status << std::endl;
#endif
if(data)
free(data);
}
iter = currentRequests.erase(iter);
}
else
{
++iter;
}
}
} }
pthread_mutex_lock(&runningMutex); pthread_mutex_lock(&runningMutex);
thumbnailQueueRunning = false; thumbnailQueueRunning = false;
pthread_mutex_unlock(&runningMutex); pthread_mutex_unlock(&runningMutex);
} }
void RequestBroker::RetrieveThumbnail(int saveID, int width, int height, ThumbnailListener * tListener) RequestBroker::ProcessResponse RequestBroker::processThumbnailRender(ThumbRenderRequest & request)
{
#ifdef DEBUG
std::cout << typeid(*this).name() << " Processing render request" << std::endl;
#endif
Thumbnail * thumbnail = SaveRenderer::Ref().Render(request.Save, request.Decorations, request.Fire);
delete request.Save;
request.Save = NULL;
if(thumbnail)
{
thumbnail->Resize(request.Width, request.Height);
request.ResultObject = (void*)thumbnail;
requestComplete(&request);
return Finished;
}
else
{
return Failed;
}
return Failed;
}
RequestBroker::ProcessResponse RequestBroker::processImage(ImageRequest & request)
{
VideoBuffer * image = NULL;
//Have a look at the thumbnail cache
for(std::deque<std::pair<std::string, VideoBuffer*> >::iterator iter = imageCache.begin(), end = imageCache.end(); iter != end; ++iter)
{
if((*iter).first == request.URL)
{
image = (*iter).second;
#ifdef DEBUG
std::cout << typeid(*this).name() << " " << request.URL << " found in cache" << std::endl;
#endif
}
}
if(!image)
{
if(request.HTTPContext)
{
if(http_async_req_status(request.HTTPContext))
{
pixel * imageData;
char * data;
int status, data_size, imgw, imgh;
data = http_async_req_stop(request.HTTPContext, &status, &data_size);
if (status == 200 && data)
{
imageData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh);
free(data);
if(imageData)
{
//Success!
image = new VideoBuffer(imageData, imgw, imgh);
free(imageData);
}
else
{
//Error thumbnail
image = new VideoBuffer(32, 32);
image->SetCharacter(14, 14, 'x', 255, 255, 255, 255);
}
if(imageCache.size() >= THUMB_CACHE_SIZE)
{
//Remove unnecessary from thumbnail cache
delete imageCache.front().second;
imageCache.pop_front();
}
imageCache.push_back(std::pair<std::string, VideoBuffer*>(request.URL, image));
}
else
{
#ifdef DEBUG
std::cout << typeid(*this).name() << " Request for " << request.URL << " failed with status " << status << std::endl;
#endif
if(data)
free(data);
return Failed;
}
}
}
else
{
//Check for ongoing requests
for(std::vector<Request*>::iterator iter = activeRequests.begin(), end = activeRequests.end(); iter != end; ++iter)
{
if((*iter)->Type != Request::Image)
continue;
ImageRequest * otherReq = (ImageRequest*)(*iter);
if(otherReq->URL == request.URL && otherReq != &request)
{
#ifdef DEBUG
std::cout << typeid(*this).name() << " Request for " << request.URL << " found, appending." << std::endl;
#endif
//Add the current listener to the item already being requested
(*iter)->Children.push_back(&request);
return Duplicate;
}
}
//If it's not already being requested, request it
#ifdef DEBUG
std::cout << typeid(*this).name() << " Creating new request for " << request.URL << std::endl;
#endif
request.HTTPContext = http_async_req_start(NULL, (char *)request.URL.c_str(), NULL, 0, 0);
request.RequestTime = time(NULL);
}
}
if(image)
{
//Create a copy, to seperate from the cache
VideoBuffer * myVB = new VideoBuffer(*image);
myVB->Resize(request.Width, request.Height, true);
request.ResultObject = (void*)myVB;
requestComplete(&request);
for(std::vector<Request*>::iterator childIter = request.Children.begin(), childEnd = request.Children.end(); childIter != childEnd; ++childIter)
{
if((*childIter)->Type == Request::Image)
{
ImageRequest * childReq = (ImageRequest*)*childIter;
VideoBuffer * tempImage = new VideoBuffer(*image);
tempImage->Resize(childReq->Width, childReq->Height, true);
childReq->ResultObject = (void*)tempImage;
requestComplete(*childIter);
}
}
return Finished;
}
return OK;
}
void RequestBroker::requestComplete(Request * completedRequest)
{
pthread_mutex_lock(&completeQueueMutex);
completeQueue.push(completedRequest);
pthread_mutex_unlock(&completeQueueMutex);
}
void RequestBroker::RetrieveThumbnail(int saveID, int width, int height, RequestListener * tListener)
{ {
RetrieveThumbnail(saveID, 0, width, height, tListener); RetrieveThumbnail(saveID, 0, width, height, tListener);
} }
bool RequestBroker::CheckThumbnailListener(ListenerHandle handle) bool RequestBroker::CheckRequestListener(ListenerHandle handle)
{ {
pthread_mutex_lock(&listenersMutex); pthread_mutex_lock(&listenersMutex);
int count = std::count(validListeners.begin(), validListeners.end(), handle); int count = std::count(validListeners.begin(), validListeners.end(), handle);
@@ -368,14 +393,16 @@ bool RequestBroker::CheckThumbnailListener(ListenerHandle handle)
return count; return count;
} }
void RequestBroker::AttachThumbnailListener(ThumbnailListener * tListener) ListenerHandle RequestBroker::AttachRequestListener(RequestListener * tListener)
{ {
ListenerHandle handle = ListenerHandle(tListener->ListenerRand, tListener);
pthread_mutex_lock(&listenersMutex); pthread_mutex_lock(&listenersMutex);
validListeners.push_back(ListenerHandle(tListener->ListenerRand, tListener)); validListeners.push_back(handle);
pthread_mutex_unlock(&listenersMutex); pthread_mutex_unlock(&listenersMutex);
return handle;
} }
void RequestBroker::DetachThumbnailListener(ThumbnailListener * tListener) void RequestBroker::DetachRequestListener(RequestListener * tListener)
{ {
pthread_mutex_lock(&listenersMutex); pthread_mutex_lock(&listenersMutex);

View File

@@ -9,113 +9,147 @@
#include "Singleton.h" #include "Singleton.h"
class GameSave; class GameSave;
class Thumbnail; class VideoBuffer;
class ThumbnailListener; class RequestListener;
typedef std::pair<int, ThumbnailListener*> ListenerHandle; typedef std::pair<int, RequestListener*> ListenerHandle;
class RequestBroker: public Singleton<RequestBroker> class RequestBroker: public Singleton<RequestBroker>
{ {
private: private:
class ThumbnailSpec
{
public:
int Width, Height;
ListenerHandle CompletedListener;
ThumbnailSpec(int width, int height, ListenerHandle completedListener) :
Width(width), Height(height), CompletedListener(completedListener) {}
};
class ThumbnailID enum ProcessResponse { Finished, OK, Canceled, Failed, Duplicate };
class Request
{ {
public: public:
int SaveID, SaveDate; enum RequestType { ThumbnailRender, Image };
bool operator ==(const ThumbnailID & second) RequestType Type;
void * ResultObject;
ListenerHandle Listener;
std::vector<Request*> Children;
Request(RequestType type, ListenerHandle listener)
{ {
return SaveID == second.SaveID && SaveDate == second.SaveDate; Type = type;
Listener = listener;
ResultObject = NULL;
} }
ThumbnailID(int saveID, int saveDate) : SaveID(saveID), SaveDate(saveDate) {} virtual ~Request()
ThumbnailID() : SaveID(0), SaveDate(0) {}
};
class ThumbnailRequest
{ {
public: std::vector<Request*>::iterator iter = Children.begin();
bool Complete; while(iter != Children.end())
void * HTTPContext;
int RequestTime;
ThumbnailID ID;
std::vector<ThumbnailSpec> SubRequests;
ThumbnailRequest(int saveID, int saveDate, int width, int height, ListenerHandle completedListener) :
ID(saveID, saveDate), Complete(false), HTTPContext(NULL), RequestTime(0)
{ {
SubRequests.push_back(ThumbnailSpec(width, height, completedListener)); delete (*iter);
iter++;
}
}
virtual void Cleanup()
{
std::vector<Request*>::iterator iter = Children.begin();
while(iter != Children.end())
{
(*iter)->Cleanup();
iter++;
}
} }
ThumbnailRequest() : Complete(false), HTTPContext(NULL), RequestTime(0) {}
}; };
class ThumbRenderRequest class ThumbRenderRequest: public Request
{ {
public: public:
int Width, Height; int Width, Height;
bool Decorations; bool Decorations;
bool Fire; bool Fire;
GameSave * Save; GameSave * Save;
ListenerHandle CompletedListener; ThumbRenderRequest(GameSave * save, bool decorations, bool fire, int width, int height, ListenerHandle listener):
ThumbRenderRequest(GameSave * save, bool decorations, bool fire, int width, int height, ListenerHandle completedListener) : Request(ThumbnailRender, listener)
Save(save), Width(width), Height(height), CompletedListener(completedListener), Decorations(decorations), Fire(fire) {}
ThumbRenderRequest() : Save(0), Decorations(true), Fire(true), Width(0), Height(0), CompletedListener(ListenerHandle(0, (ThumbnailListener*)NULL)) {}
};
class Request
{ {
enum RequestType { Thumbnail, ThumbnailRender, HTTP }; Save = save;
public: Width = width;
RequestType Type; Height = height;
void * RequestObject; Decorations = decorations;
ListenerHandle Listener; Fire = fire;
}
virtual ~ThumbRenderRequest()
{
if(Save)
delete Save;
}
virtual void Cleanup()
{
Request::Cleanup();
if(ResultObject)
{
delete ((VideoBuffer*)ResultObject);
ResultObject = NULL;
}
}
}; };
//Thumbnail retreival class ImageRequest: public Request
/*int thumbnailCacheNextID; {
Thumbnail * thumbnailCache[THUMB_CACHE_SIZE]; public:
void * activeThumbRequests[IMGCONNS]; int Width, Height;
int activeThumbRequestTimes[IMGCONNS]; std::string URL;
int activeThumbRequestCompleteTimes[IMGCONNS]; int RequestTime;
std::string activeThumbRequestIDs[IMGCONNS];*/ void * HTTPContext;
ImageRequest(std::string url, int width, int height, ListenerHandle listener):
Request(Image, listener)
{
URL = url;
HTTPContext = NULL;
Width = width;
Height = height;
}
virtual ~ImageRequest() {}
virtual void Cleanup()
{
Request::Cleanup();
if(ResultObject)
{
delete ((VideoBuffer*)ResultObject);
ResultObject = NULL;
}
}
};
pthread_mutex_t thumbnailQueueMutex;
pthread_mutex_t listenersMutex; pthread_mutex_t listenersMutex;
pthread_mutex_t runningMutex; pthread_mutex_t runningMutex;
pthread_mutex_t requestQueueMutex;
pthread_mutex_t completeQueueMutex;
pthread_t thumbnailQueueThread; pthread_t thumbnailQueueThread;
bool thumbnailQueueRunning; bool thumbnailQueueRunning;
std::deque<ThumbnailRequest> thumbnailRequests;
std::deque<ThumbRenderRequest> renderRequests;
std::deque<std::pair<ListenerHandle, Thumbnail*> > thumbnailComplete;
std::list<ThumbnailRequest> currentRequests;
std::deque<std::pair<ThumbnailID, Thumbnail*> > thumbnailCache;
std::vector<ListenerHandle> validListeners; std::vector<ListenerHandle> validListeners;
std::deque<Request> requestQueue; std::deque<std::pair<std::string, VideoBuffer*> > imageCache;
std::queue<Request*> completeQueue;
std::vector<Request*> requestQueue;
std::vector<Request*> activeRequests;
static void * thumbnailQueueProcessHelper(void * ref); static void * thumbnailQueueProcessHelper(void * ref);
void thumbnailQueueProcessTH(); void thumbnailQueueProcessTH();
void assureRunning(); void assureRunning();
ProcessResponse processThumbnailRender(ThumbRenderRequest & request);
ProcessResponse processImage(ImageRequest & request);
void requestComplete(Request * completedRequest);
public: public:
RequestBroker(); RequestBroker();
virtual ~RequestBroker(); virtual ~RequestBroker();
void Shutdown(); void Shutdown();
void FlushThumbQueue(); void FlushThumbQueue();
void RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, ThumbnailListener * tListener); void RetrieveImage(std::string imageUrl, int width, int height, RequestListener * tListener);
void RenderThumbnail(GameSave * gameSave, int width, int height, ThumbnailListener * tListener); void RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, RequestListener * tListener);
void RetrieveThumbnail(int saveID, int saveDate, int width, int height, ThumbnailListener * tListener); void RenderThumbnail(GameSave * gameSave, int width, int height, RequestListener * tListener);
void RetrieveThumbnail(int saveID, int width, int height, ThumbnailListener * tListener); void RetrieveThumbnail(int saveID, int saveDate, int width, int height, RequestListener * tListener);
void RetrieveThumbnail(int saveID, int width, int height, RequestListener * tListener);
void RetrieveAvatar(std::string username, int width, int height, RequestListener * tListener);
bool CheckThumbnailListener(ListenerHandle handle); bool CheckRequestListener(ListenerHandle handle);
void AttachThumbnailListener(ThumbnailListener * tListener); ListenerHandle AttachRequestListener(RequestListener * tListener);
void DetachThumbnailListener(ThumbnailListener * tListener); void DetachRequestListener(RequestListener * tListener);
}; };

View File

@@ -0,0 +1,11 @@
#pragma once
class RequestListener
{
public:
int ListenerRand;
RequestListener() { ListenerRand = rand(); }
virtual ~RequestListener() {}
virtual void OnResponseReady(void * response) {}
};

View File

@@ -1,12 +0,0 @@
#pragma once
class Thumbnail;
class ThumbnailListener
{
public:
int ListenerRand;
ThumbnailListener() { ListenerRand = rand(); }
virtual ~ThumbnailListener() {}
virtual void OnThumbnailReady(Thumbnail * thumb) {}
};

View File

@@ -5,6 +5,7 @@
#include "Format.h" #include "Format.h"
#include "Engine.h" #include "Engine.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/RequestBroker.h"
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
#include "ContextMenu.h" #include "ContextMenu.h"
#include "Keys.h" #include "Keys.h"
@@ -23,6 +24,7 @@ AvatarButton::AvatarButton(Point position, Point size, std::string username):
AvatarButton::~AvatarButton() AvatarButton::~AvatarButton()
{ {
RequestBroker::Ref().DetachRequestListener(this);
if(avatar) if(avatar)
delete avatar; delete avatar;
if(actionCallback) if(actionCallback)
@@ -34,13 +36,18 @@ void AvatarButton::Tick(float dt)
if(!avatar && !tried && name.size() > 0) if(!avatar && !tried && name.size() > 0)
{ {
tried = true; tried = true;
avatar = Client::Ref().GetAvatar(name); RequestBroker::Ref().RetrieveAvatar(name, Size.X, Size.Y, this);
if(avatar) { }
if(avatar->Width != Size.X && avatar->Height != Size.Y) }
void AvatarButton::OnResponseReady(void * imagePtr)
{
VideoBuffer * image = (VideoBuffer*)imagePtr;
if(image)
{ {
avatar->Resize(Size.X, Size.Y, true); if(avatar)
} delete avatar;
} avatar = image;
} }
} }

View File

@@ -6,6 +6,7 @@
#include "Component.h" #include "Component.h"
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
#include "interface/Colour.h" #include "interface/Colour.h"
#include "client/RequestListener.h"
namespace ui namespace ui
{ {
@@ -17,7 +18,7 @@ public:
virtual ~AvatarButtonAction() {} virtual ~AvatarButtonAction() {}
}; };
class AvatarButton : public Component class AvatarButton : public Component, public RequestListener
{ {
VideoBuffer * avatar; VideoBuffer * avatar;
std::string name; std::string name;
@@ -37,6 +38,8 @@ public:
virtual void Draw(const Point& screenPos); virtual void Draw(const Point& screenPos);
virtual void Tick(float dt); virtual void Tick(float dt);
virtual void OnResponseReady(void * imagePtr);
virtual void DoAction(); virtual void DoAction();
void SetUsername(std::string username) { name = username; } void SetUsername(std::string username) { name = username; }

View File

@@ -117,7 +117,7 @@ SaveButton::SaveButton(Point position, Point size, SaveFile * file):
SaveButton::~SaveButton() SaveButton::~SaveButton()
{ {
RequestBroker::Ref().DetachThumbnailListener(this); RequestBroker::Ref().DetachRequestListener(this);
if(thumbnail) if(thumbnail)
delete thumbnail; delete thumbnail;
@@ -129,13 +129,14 @@ SaveButton::~SaveButton()
delete file; delete file;
} }
void SaveButton::OnThumbnailReady(Thumbnail * thumb) void SaveButton::OnResponseReady(void * imagePtr)
{ {
if(thumb) VideoBuffer * image = (VideoBuffer*)imagePtr;
if(image)
{ {
if(thumbnail) if(thumbnail)
delete thumbnail; delete thumbnail;
thumbnail = thumb; thumbnail = image;
waitingForThumb = false; waitingForThumb = false;
} }
} }
@@ -144,23 +145,25 @@ void SaveButton::Tick(float dt)
{ {
if(!thumbnail && !waitingForThumb) if(!thumbnail && !waitingForThumb)
{ {
float scaleFactor = (Size.Y-25)/((float)YRES);
ui::Point thumbBoxSize = ui::Point(((float)XRES)*scaleFactor, ((float)YRES)*scaleFactor);
if(save) if(save)
{ {
if(save->GetGameSave()) if(save->GetGameSave())
{ {
waitingForThumb = true; waitingForThumb = true;
RequestBroker::Ref().RenderThumbnail(save->GetGameSave(), Size.X-3, Size.Y-25, this); RequestBroker::Ref().RenderThumbnail(save->GetGameSave(), thumbBoxSize.X, thumbBoxSize.Y, this);
} }
else if(save->GetID()) else if(save->GetID())
{ {
waitingForThumb = true; waitingForThumb = true;
RequestBroker::Ref().RetrieveThumbnail(save->GetID(), save->GetVersion(), Size.X-3, Size.Y-25, this); RequestBroker::Ref().RetrieveThumbnail(save->GetID(), save->GetVersion(), thumbBoxSize.X, thumbBoxSize.Y, this);
} }
} }
else if(file && file->GetGameSave()) else if(file && file->GetGameSave())
{ {
waitingForThumb = true; waitingForThumb = true;
RequestBroker::Ref().RenderThumbnail(file->GetGameSave(), Size.X-3, Size.Y-25, this); RequestBroker::Ref().RenderThumbnail(file->GetGameSave(), thumbBoxSize.X, thumbBoxSize.Y, this);
} }
} }
} }
@@ -180,11 +183,11 @@ void SaveButton::Draw(const Point& screenPos)
if(thumbnail) if(thumbnail)
{ {
thumbBoxSize = ui::Point(thumbnail->Size.X, thumbnail->Size.Y); thumbBoxSize = ui::Point(thumbnail->Width, thumbnail->Height);
if(save && save->id) if(save && save->id)
g->draw_image(thumbnail->Data, screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbnail->Size.X, thumbnail->Size.Y, 255); g->draw_image(thumbnail, screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 255);
else else
g->draw_image(thumbnail->Data, screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbnail->Size.X, thumbnail->Size.Y, 255); g->draw_image(thumbnail, screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 255);
} }
else else
{ {

View File

@@ -6,9 +6,8 @@
#include "Component.h" #include "Component.h"
#include "client/SaveFile.h" #include "client/SaveFile.h"
#include "client/SaveInfo.h" #include "client/SaveInfo.h"
#include "client/ThumbnailListener.h" #include "client/RequestListener.h"
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
#include "search/Thumbnail.h"
#include "interface/Colour.h" #include "interface/Colour.h"
namespace ui namespace ui
@@ -24,11 +23,11 @@ public:
virtual ~SaveButtonAction() {} virtual ~SaveButtonAction() {}
}; };
class SaveButton : public Component, public ThumbnailListener class SaveButton : public Component, public RequestListener
{ {
SaveFile * file; SaveFile * file;
SaveInfo * save; SaveInfo * save;
Thumbnail * thumbnail; VideoBuffer * thumbnail;
std::string name; std::string name;
std::string votesString; std::string votesString;
std::string votesBackground; std::string votesBackground;
@@ -59,7 +58,7 @@ public:
virtual void Draw(const Point& screenPos); virtual void Draw(const Point& screenPos);
virtual void Tick(float dt); virtual void Tick(float dt);
virtual void OnThumbnailReady(Thumbnail * thumb); virtual void OnResponseReady(void * imagePtr);
void SetSelected(bool selected_) { selected = selected_; } void SetSelected(bool selected_) { selected = selected_; }
bool GetSelected() { return selected; } bool GetSelected() { return selected; }

View File

@@ -543,15 +543,15 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
int usernameY = currentY+5, commentY; int usernameY = currentY+5, commentY;
if(showAvatars) if(showAvatars)
{ {
tempAvatar = new ui::AvatarButton(ui::Point(4, currentY+4), ui::Point(26, 26), comments->at(i)->authorName); tempAvatar = new ui::AvatarButton(ui::Point(2, currentY+7), ui::Point(26, 26), comments->at(i)->authorName);
commentComponents.push_back(tempAvatar); commentComponents.push_back(tempAvatar);
commentsPanel->AddChild(tempAvatar); commentsPanel->AddChild(tempAvatar);
} }
if(showAvatars) if(showAvatars)
tempUsername = new ui::Label(ui::Point(31, currentY+5), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorName); tempUsername = new ui::Label(ui::Point(31, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorName);
else else
tempUsername = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorName); tempUsername = new ui::Label(ui::Point(5, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorName);
tempUsername->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempUsername->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
tempUsername->Appearance.VerticalAlign = ui::Appearance::AlignBottom; tempUsername->Appearance.VerticalAlign = ui::Appearance::AlignBottom;
currentY += 16; currentY += 16;
@@ -560,6 +560,9 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
commentsPanel->AddChild(tempUsername); commentsPanel->AddChild(tempUsername);
commentY = currentY+5; commentY = currentY+5;
if(showAvatars)
tempComment = new ui::Label(ui::Point(31, currentY+5), ui::Point(Size.X-((XRES/2) + 13 + 26), -1), comments->at(i)->comment);
else
tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), -1), comments->at(i)->comment); tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), -1), comments->at(i)->comment);
tempComment->SetMultiline(true); tempComment->SetMultiline(true);
tempComment->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempComment->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;

View File

@@ -123,14 +123,14 @@ void LocalSaveActivity::OnDraw()
if(thumbnail) if(thumbnail)
{ {
g->draw_image(thumbnail->Data, Position.X+(Size.X-thumbnail->Size.X)/2, Position.Y+45, thumbnail->Size.X, thumbnail->Size.Y, 255); g->draw_image(thumbnail, Position.X+(Size.X-thumbnail->Width)/2, Position.Y+45, 255);
g->drawrect(Position.X+(Size.X-thumbnail->Size.X)/2, Position.Y+45, thumbnail->Size.X, thumbnail->Size.Y, 180, 180, 180, 255); g->drawrect(Position.X+(Size.X-thumbnail->Width)/2, Position.Y+45, thumbnail->Width, thumbnail->Height, 180, 180, 180, 255);
} }
} }
void LocalSaveActivity::OnThumbnailReady(Thumbnail * thumbnail) void LocalSaveActivity::OnRequestReady(void * imagePtr)
{ {
this->thumbnail = thumbnail; this->thumbnail = (VideoBuffer*)imagePtr;
} }
LocalSaveActivity::~LocalSaveActivity() LocalSaveActivity::~LocalSaveActivity()

View File

@@ -2,14 +2,14 @@
#include "Activity.h" #include "Activity.h"
#include "client/SaveFile.h" #include "client/SaveFile.h"
#include "client/ThumbnailListener.h" #include "client/RequestListener.h"
namespace ui namespace ui
{ {
class Textbox; class Textbox;
} }
class Thumbnail; class VideoBuffer;
class FileSavedCallback class FileSavedCallback
{ {
@@ -19,10 +19,10 @@ public:
virtual void FileSaved(SaveFile * file) {} virtual void FileSaved(SaveFile * file) {}
}; };
class LocalSaveActivity: public WindowActivity, public ThumbnailListener class LocalSaveActivity: public WindowActivity, public RequestListener
{ {
SaveFile save; SaveFile save;
Thumbnail * thumbnail; VideoBuffer * thumbnail;
ui::Textbox * filenameField; ui::Textbox * filenameField;
class CancelAction; class CancelAction;
class SaveAction; class SaveAction;
@@ -34,6 +34,6 @@ public:
void saveWrite(std::string finalFilename); void saveWrite(std::string finalFilename);
virtual void Save(); virtual void Save();
virtual void OnDraw(); virtual void OnDraw();
virtual void OnThumbnailReady(Thumbnail * thumbnail); virtual void OnRequestReady(void * imagePtr);
virtual ~LocalSaveActivity(); virtual ~LocalSaveActivity();
}; };

View File

@@ -3,7 +3,6 @@
#include "interface/Textbox.h" #include "interface/Textbox.h"
#include "interface/Button.h" #include "interface/Button.h"
#include "interface/Checkbox.h" #include "interface/Checkbox.h"
#include "search/Thumbnail.h"
#include "client/RequestBroker.h" #include "client/RequestBroker.h"
#include "dialogues/ErrorMessage.h" #include "dialogues/ErrorMessage.h"
#include "dialogues/ConfirmPrompt.h" #include "dialogues/ConfirmPrompt.h"
@@ -245,14 +244,14 @@ void ServerSaveActivity::OnDraw()
if(thumbnail) if(thumbnail)
{ {
g->draw_image(thumbnail->Data, Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Size.X)/2, Position.Y+25, thumbnail->Size.X, thumbnail->Size.Y, 255); g->draw_image(thumbnail, Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Width)/2, Position.Y+25, 255);
g->drawrect(Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Size.X)/2, Position.Y+25, thumbnail->Size.X, thumbnail->Size.Y, 180, 180, 180, 255); g->drawrect(Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Width)/2, Position.Y+25, thumbnail->Width, thumbnail->Height, 180, 180, 180, 255);
} }
} }
void ServerSaveActivity::OnThumbnailReady(Thumbnail * thumbnail) void ServerSaveActivity::OnRequestReady(void * imagePtr)
{ {
this->thumbnail = thumbnail; this->thumbnail = (VideoBuffer *)imagePtr;
} }
ServerSaveActivity::~ServerSaveActivity() ServerSaveActivity::~ServerSaveActivity()

View File

@@ -2,7 +2,7 @@
#include "Activity.h" #include "Activity.h"
#include "client/SaveInfo.h" #include "client/SaveInfo.h"
#include "client/ThumbnailListener.h" #include "client/RequestListener.h"
#include "tasks/TaskListener.h" #include "tasks/TaskListener.h"
namespace ui namespace ui
@@ -13,7 +13,7 @@ namespace ui
class Task; class Task;
class Thumbnail; class Thumbnail;
class ServerSaveActivity: public WindowActivity, public ThumbnailListener, public TaskListener class ServerSaveActivity: public WindowActivity, public RequestListener, public TaskListener
{ {
public: public:
class SaveUploadedCallback class SaveUploadedCallback
@@ -29,7 +29,7 @@ public:
virtual void Save(); virtual void Save();
virtual void Exit(); virtual void Exit();
virtual void OnDraw(); virtual void OnDraw();
virtual void OnThumbnailReady(Thumbnail * thumbnail); virtual void OnRequestReady(void * imagePtr);
virtual void OnTick(float dt); virtual void OnTick(float dt);
virtual ~ServerSaveActivity(); virtual ~ServerSaveActivity();
protected: protected:
@@ -37,7 +37,7 @@ protected:
Task * saveUploadTask; Task * saveUploadTask;
SaveUploadedCallback * callback; SaveUploadedCallback * callback;
SaveInfo save; SaveInfo save;
Thumbnail * thumbnail; VideoBuffer * thumbnail;
ui::Textbox * nameField; ui::Textbox * nameField;
ui::Textbox * descriptionField; ui::Textbox * descriptionField;
ui::Checkbox * publishedCheckbox; ui::Checkbox * publishedCheckbox;