summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Format.cpp15
-rw-r--r--src/Format.h1
-rw-r--r--src/client/Client.cpp28
-rw-r--r--src/client/Client.h5
-rw-r--r--src/client/RequestBroker.cpp419
-rw-r--r--src/client/RequestBroker.h155
-rw-r--r--src/client/RequestListener.h11
-rw-r--r--src/client/ThumbnailBroker.cpp395
-rw-r--r--src/client/ThumbnailBroker.h111
-rw-r--r--src/client/ThumbnailListener.h12
-rw-r--r--src/graphics/Graphics.cpp15
-rw-r--r--src/graphics/Graphics.h2
-rw-r--r--src/interface/AvatarButton.cpp117
-rw-r--r--src/interface/AvatarButton.h54
-rw-r--r--src/interface/SaveButton.cpp25
-rw-r--r--src/interface/SaveButton.h9
-rw-r--r--src/preview/PreviewView.cpp53
-rw-r--r--src/preview/PreviewView.h3
-rw-r--r--src/save/LocalSaveActivity.cpp12
-rw-r--r--src/save/LocalSaveActivity.h10
-rw-r--r--src/save/ServerSaveActivity.cpp13
-rw-r--r--src/save/ServerSaveActivity.h8
22 files changed, 905 insertions, 568 deletions
diff --git a/src/Format.cpp b/src/Format.cpp
index f7c3c09..e4056c2 100644
--- a/src/Format.cpp
+++ b/src/Format.cpp
@@ -138,6 +138,21 @@ std::vector<char> format::VideoBufferToPTI(const VideoBuffer & vidBuf)
return data;
}
+VideoBuffer * format::PTIToVideoBuffer(std::vector<char> & data)
+{
+
+ int newWidth, newHeight;
+ pixel * buffer = Graphics::ptif_unpack(&data[0], data.size(), &newWidth, &newHeight);
+
+ if(buffer)
+ {
+ VideoBuffer * vb = new VideoBuffer(buffer, newWidth, newHeight);
+ free(buffer);
+ return vb;
+ }
+ return NULL;
+}
+
std::vector<char> format::VideoBufferToPPM(const VideoBuffer & vidBuf)
{
std::vector<char> data;
diff --git a/src/Format.h b/src/Format.h
index 0fd63a1..a791511 100644
--- a/src/Format.h
+++ b/src/Format.h
@@ -33,5 +33,6 @@ namespace format
std::vector<char> VideoBufferToPNG(const VideoBuffer & vidBuf);
std::vector<char> VideoBufferToPPM(const VideoBuffer & vidBuf);
std::vector<char> VideoBufferToPTI(const VideoBuffer & vidBuf);
+ VideoBuffer * PTIToVideoBuffer(std::vector<char> & data);
unsigned long CalculateCRC(unsigned char * data, int length);
} \ No newline at end of file
diff --git a/src/client/Client.cpp b/src/client/Client.cpp
index af650a9..78a7e2e 100644
--- a/src/client/Client.cpp
+++ b/src/client/Client.cpp
@@ -42,7 +42,7 @@
#include "search/Thumbnail.h"
#include "preview/Comment.h"
#include "ClientListener.h"
-#include "ThumbnailBroker.h"
+#include "RequestBroker.h"
#include "cajun/reader.h"
#include "cajun/writer.h"
@@ -634,7 +634,7 @@ std::vector<std::pair<std::string, std::string> > Client::GetServerNotifications
void Client::Tick()
{
//Check thumbnail queue
- ThumbnailBroker::Ref().FlushThumbQueue();
+ RequestBroker::Ref().FlushThumbQueue();
//Check status on version check request
if(versionCheckRequest && http_async_req_status(versionCheckRequest))
@@ -826,7 +826,7 @@ void Client::WritePrefs()
void Client::Shutdown()
{
- ThumbnailBroker::Ref().Shutdown();
+ RequestBroker::Ref().Shutdown();
ClearThumbnailRequests();
http_done();
@@ -1188,6 +1188,28 @@ std::vector<unsigned char> Client::GetSaveData(int saveID, int saveDate)
return saveData;
}
+VideoBuffer * Client::GetAvatar(std::string username)
+{
+ lastError = "";
+ int dataStatus;
+ int dataLength = 0;
+ unsigned char * data;
+ std::stringstream urlStream;
+ urlStream << "http://" << STATICSERVER << "/avatars/" << username << ".pti";
+
+ data = (unsigned char *)http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength);
+ if(data && dataStatus == 200)
+ {
+ std::vector<char> responseData(data, data+dataLength);
+ return format::PTIToVideoBuffer(responseData);
+ }
+ else if(data)
+ {
+ free(data);
+ }
+ return NULL;
+}
+
LoginStatus Client::Login(std::string username, std::string password, User & user)
{
lastError = "";
diff --git a/src/client/Client.h b/src/client/Client.h
index 0b40012..c824d28 100644
--- a/src/client/Client.h
+++ b/src/client/Client.h
@@ -17,6 +17,7 @@ class SaveInfo;
class SaveFile;
class SaveComment;
class GameSave;
+class VideoBuffer;
enum LoginStatus {
LoginOkay, LoginError
@@ -41,7 +42,7 @@ public:
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 Client: public Singleton<Client> {
private:
@@ -125,6 +126,8 @@ public:
RequestStatus AddComment(int saveID, std::string comment);
+ VideoBuffer * GetAvatar(std::string username);
+
unsigned char * GetSaveData(int saveID, int saveDate, int & dataLength);
std::vector<unsigned char> GetSaveData(int saveID, int saveDate);
LoginStatus Login(std::string username, std::string password, User & user);
diff --git a/src/client/RequestBroker.cpp b/src/client/RequestBroker.cpp
new file mode 100644
index 0000000..8ecd5cc
--- /dev/null
+++ b/src/client/RequestBroker.cpp
@@ -0,0 +1,419 @@
+#include <algorithm>
+#include <iostream>
+#include <typeinfo>
+#include <time.h>
+#include "RequestBroker.h"
+#include "RequestListener.h"
+#include "Client.h"
+#include "HTTP.h"
+#include "GameSave.h"
+#include "search/Thumbnail.h"
+#include "simulation/SaveRenderer.h"
+
+//Asynchronous Thumbnail render & request processing
+
+RequestBroker::RequestBroker()
+{
+ thumbnailQueueRunning = false;
+
+ //listenersMutex = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_init (&listenersMutex, NULL);
+
+
+ pthread_mutex_init (&runningMutex, NULL);
+
+
+ pthread_mutex_init (&requestQueueMutex, NULL);
+
+
+ pthread_mutex_init (&completeQueueMutex, NULL);
+}
+
+RequestBroker::~RequestBroker()
+{
+
+}
+
+void RequestBroker::assureRunning()
+{
+ pthread_mutex_lock(&runningMutex);
+ bool running = thumbnailQueueRunning;
+ thumbnailQueueRunning = true;
+ pthread_mutex_unlock(&runningMutex);
+
+ if(!running)
+ {
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Starting background thread for new " << __FUNCTION__ << " request" << std::endl;
+#endif
+ pthread_create(&thumbnailQueueThread, 0, &RequestBroker::thumbnailQueueProcessHelper, this);
+ }
+}
+
+void RequestBroker::Shutdown()
+{
+ pthread_mutex_lock(&runningMutex);
+ if(thumbnailQueueRunning)
+ {
+ thumbnailQueueRunning = false;
+ pthread_mutex_unlock(&runningMutex);
+ pthread_join(thumbnailQueueThread, NULL);
+ }
+ else
+ pthread_mutex_unlock(&runningMutex);
+
+ std::vector<Request*>::iterator req = activeRequests.begin();
+ while(req != activeRequests.end())
+ {
+ (*req)->Cleanup();
+ delete (*req);
+ req++;
+ }
+}
+
+void RequestBroker::RenderThumbnail(GameSave * gameSave, int width, int height, RequestListener * tListener)
+{
+ RenderThumbnail(gameSave, true, true, width, height, tListener);
+}
+
+void RequestBroker::RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, RequestListener * tListener)
+{
+ ListenerHandle handle = AttachRequestListener(tListener);
+
+ ThumbRenderRequest * r = new ThumbRenderRequest(new GameSave(*gameSave), decorations, fire, width, height, handle);
+
+ pthread_mutex_lock(&requestQueueMutex);
+ requestQueue.push_back(r);
+ pthread_mutex_unlock(&requestQueueMutex);
+
+ assureRunning();
+}
+
+void RequestBroker::RetrieveThumbnail(int saveID, int saveDate, int width, int height, RequestListener * tListener)
+{
+ std::stringstream urlStream;
+ urlStream << "http://" << STATICSERVER << "/" << saveID;
+ if(saveDate)
+ {
+ 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();
+}
+
+void * RequestBroker::thumbnailQueueProcessHelper(void * ref)
+{
+ ((RequestBroker*)ref)->thumbnailQueueProcessTH();
+ return NULL;
+}
+
+void RequestBroker::FlushThumbQueue()
+{
+ pthread_mutex_lock(&completeQueueMutex);
+ while(completeQueue.size())
+ {
+ if(CheckRequestListener(completeQueue.front()->Listener))
+ {
+ completeQueue.front()->Listener.second->OnResponseReady(completeQueue.front()->ResultObject);
+ }
+ else
+ {
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Listener lost, discarding request" << std::endl;
+#endif
+ completeQueue.front()->Cleanup();
+ }
+ delete completeQueue.front();
+ completeQueue.pop();
+ }
+ pthread_mutex_unlock(&completeQueueMutex);
+}
+
+void RequestBroker::thumbnailQueueProcessTH()
+{
+ time_t lastAction = time(NULL);
+ pthread_mutex_lock(&runningMutex);
+ thumbnailQueueRunning = true;
+ pthread_mutex_unlock(&runningMutex);
+ while(true)
+ {
+ //Shutdown after 2 seconds of idle
+ if(time(NULL) - lastAction > 2)
+ {
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Idle shutdown" << std::endl;
+#endif
+ break;
+ }
+
+
+ pthread_mutex_lock(&runningMutex);
+ bool running = thumbnailQueueRunning;
+ pthread_mutex_unlock(&runningMutex);
+ if(!running)
+ {
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Requested shutdown" << std::endl;
+#endif
+ break;
+ }
+
+ if(activeRequests.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);
+ }
+
+ //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
+ {
+ activeRequests.push_back(*newReq);
+ newReq = requestQueue.erase(newReq);
+ }
+ }
+ pthread_mutex_unlock(&requestQueueMutex);
+ }
+ pthread_mutex_lock(&runningMutex);
+ thumbnailQueueRunning = false;
+ pthread_mutex_unlock(&runningMutex);
+}
+
+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);
+}
+
+bool RequestBroker::CheckRequestListener(ListenerHandle handle)
+{
+ pthread_mutex_lock(&listenersMutex);
+ int count = std::count(validListeners.begin(), validListeners.end(), handle);
+ pthread_mutex_unlock(&listenersMutex);
+
+ return count;
+}
+
+ListenerHandle RequestBroker::AttachRequestListener(RequestListener * tListener)
+{
+ ListenerHandle handle = ListenerHandle(tListener->ListenerRand, tListener);
+ pthread_mutex_lock(&listenersMutex);
+ validListeners.push_back(handle);
+ pthread_mutex_unlock(&listenersMutex);
+ return handle;
+}
+
+void RequestBroker::DetachRequestListener(RequestListener * tListener)
+{
+ pthread_mutex_lock(&listenersMutex);
+
+ std::vector<ListenerHandle>::iterator iter = validListeners.begin();
+ while (iter != validListeners.end())
+ {
+ if(*iter == ListenerHandle(tListener->ListenerRand, tListener))
+ iter = validListeners.erase(iter);
+ else
+ ++iter;
+ }
+
+ pthread_mutex_unlock(&listenersMutex);
+} \ No newline at end of file
diff --git a/src/client/RequestBroker.h b/src/client/RequestBroker.h
new file mode 100644
index 0000000..9244e91
--- /dev/null
+++ b/src/client/RequestBroker.h
@@ -0,0 +1,155 @@
+#pragma once
+#include <queue>
+#include <list>
+#include <utility>
+#include <deque>
+#include <pthread.h>
+#undef GetUserName //God dammit microsoft!
+
+#include "Singleton.h"
+
+class GameSave;
+class VideoBuffer;
+class RequestListener;
+typedef std::pair<int, RequestListener*> ListenerHandle;
+class RequestBroker: public Singleton<RequestBroker>
+{
+private:
+
+ enum ProcessResponse { Finished, OK, Canceled, Failed, Duplicate };
+
+ class Request
+ {
+ public:
+ enum RequestType { ThumbnailRender, Image };
+ RequestType Type;
+ void * ResultObject;
+ ListenerHandle Listener;
+ std::vector<Request*> Children;
+ Request(RequestType type, ListenerHandle listener)
+ {
+ Type = type;
+ Listener = listener;
+ ResultObject = NULL;
+ }
+ virtual ~Request()
+ {
+ std::vector<Request*>::iterator iter = Children.begin();
+ while(iter != Children.end())
+ {
+ delete (*iter);
+ iter++;
+ }
+ }
+ virtual void Cleanup()
+ {
+ std::vector<Request*>::iterator iter = Children.begin();
+ while(iter != Children.end())
+ {
+ (*iter)->Cleanup();
+ iter++;
+ }
+ }
+ };
+
+ class ThumbRenderRequest: public Request
+ {
+ public:
+ int Width, Height;
+ bool Decorations;
+ bool Fire;
+ GameSave * Save;
+ ThumbRenderRequest(GameSave * save, bool decorations, bool fire, int width, int height, ListenerHandle listener):
+ Request(ThumbnailRender, listener)
+ {
+ Save = save;
+ Width = width;
+ Height = height;
+ Decorations = decorations;
+ Fire = fire;
+ }
+ virtual ~ThumbRenderRequest()
+ {
+ if(Save)
+ delete Save;
+ }
+ virtual void Cleanup()
+ {
+ Request::Cleanup();
+ if(ResultObject)
+ {
+ delete ((VideoBuffer*)ResultObject);
+ ResultObject = NULL;
+ }
+ }
+ };
+
+ class ImageRequest: public Request
+ {
+ public:
+ int Width, Height;
+ std::string URL;
+ int RequestTime;
+ 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 listenersMutex;
+ pthread_mutex_t runningMutex;
+ pthread_mutex_t requestQueueMutex;
+ pthread_mutex_t completeQueueMutex;
+
+ pthread_t thumbnailQueueThread;
+ bool thumbnailQueueRunning;
+
+ std::vector<ListenerHandle> validListeners;
+
+ 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);
+ void thumbnailQueueProcessTH();
+ void assureRunning();
+
+ ProcessResponse processThumbnailRender(ThumbRenderRequest & request);
+ ProcessResponse processImage(ImageRequest & request);
+
+ void requestComplete(Request * completedRequest);
+
+public:
+ RequestBroker();
+ virtual ~RequestBroker();
+ void Shutdown();
+
+ void FlushThumbQueue();
+ void RetrieveImage(std::string imageUrl, int width, int height, RequestListener * tListener);
+ void RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, RequestListener * tListener);
+ void RenderThumbnail(GameSave * gameSave, int width, int height, RequestListener * 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 CheckRequestListener(ListenerHandle handle);
+ ListenerHandle AttachRequestListener(RequestListener * tListener);
+ void DetachRequestListener(RequestListener * tListener);
+}; \ No newline at end of file
diff --git a/src/client/RequestListener.h b/src/client/RequestListener.h
new file mode 100644
index 0000000..1f53b2a
--- /dev/null
+++ b/src/client/RequestListener.h
@@ -0,0 +1,11 @@
+#pragma once
+
+class RequestListener
+{
+public:
+ int ListenerRand;
+ RequestListener() { ListenerRand = rand(); }
+ virtual ~RequestListener() {}
+
+ virtual void OnResponseReady(void * response) {}
+};
diff --git a/src/client/ThumbnailBroker.cpp b/src/client/ThumbnailBroker.cpp
deleted file mode 100644
index 390af13..0000000
--- a/src/client/ThumbnailBroker.cpp
+++ /dev/null
@@ -1,395 +0,0 @@
-#include <algorithm>
-#include <iostream>
-#include <typeinfo>
-#include <time.h>
-#include "ThumbnailBroker.h"
-#include "ThumbnailListener.h"
-#include "Client.h"
-#include "HTTP.h"
-#include "GameSave.h"
-#include "search/Thumbnail.h"
-#include "simulation/SaveRenderer.h"
-
-//Asynchronous Thumbnail render & request processing
-
-ThumbnailBroker::ThumbnailBroker()
-{
- thumbnailQueueRunning = false;
- //thumbnailQueueMutex = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_init (&thumbnailQueueMutex, NULL);
-
- //listenersMutex = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_init (&listenersMutex, NULL);
-
-
- pthread_mutex_init (&runningMutex, NULL);
-}
-
-ThumbnailBroker::~ThumbnailBroker()
-{
- for(std::deque<std::pair<ThumbnailID, Thumbnail*> >::iterator iter = thumbnailCache.begin(), end = thumbnailCache.end(); iter != end; ++iter)
- {
- delete (*iter).second;
- }
-}
-
-void ThumbnailBroker::assureRunning()
-{
- pthread_mutex_lock(&runningMutex);
- bool running = thumbnailQueueRunning;
- thumbnailQueueRunning = true;
- pthread_mutex_unlock(&runningMutex);
-
- if(!running)
- {
-#ifdef DEBUG
- std::cout << typeid(*this).name() << " Starting background thread for new " << __FUNCTION__ << " request" << std::endl;
-#endif
- pthread_create(&thumbnailQueueThread, 0, &ThumbnailBroker::thumbnailQueueProcessHelper, this);
- }
-}
-
-void ThumbnailBroker::Shutdown()
-{
- pthread_mutex_lock(&runningMutex);
- if(thumbnailQueueRunning)
- {
- thumbnailQueueRunning = false;
- pthread_mutex_unlock(&runningMutex);
- pthread_join(thumbnailQueueThread, NULL);
- }
- else
- pthread_mutex_unlock(&runningMutex);
-
-
- for (std::list<ThumbnailRequest>::iterator iter = currentRequests.begin(), end = currentRequests.end(); iter != end; ++iter)
- {
- ThumbnailRequest req = *iter;
- if(req.HTTPContext)
- {
- http_async_req_close(req.HTTPContext);
- }
- }
-}
-
-void ThumbnailBroker::RenderThumbnail(GameSave * gameSave, int width, int height, ThumbnailListener * tListener)
-{
- RenderThumbnail(gameSave, true, true, width, height, tListener);
-}
-
-void ThumbnailBroker::RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, ThumbnailListener * tListener)
-{
- AttachThumbnailListener(tListener);
- pthread_mutex_lock(&thumbnailQueueMutex);
- renderRequests.push_back(ThumbRenderRequest(new GameSave(*gameSave), decorations, fire, width, height, ListenerHandle(tListener->ListenerRand, tListener)));
- pthread_mutex_unlock(&thumbnailQueueMutex);
-
- assureRunning();
-}
-
-void ThumbnailBroker::RetrieveThumbnail(int saveID, int saveDate, int width, int height, ThumbnailListener * tListener)
-{
- AttachThumbnailListener(tListener);
- pthread_mutex_lock(&thumbnailQueueMutex);
- thumbnailRequests.push_back(ThumbnailRequest(saveID, saveDate, width, height, ListenerHandle(tListener->ListenerRand, tListener)));
- pthread_mutex_unlock(&thumbnailQueueMutex);
-
- assureRunning();
-}
-
-void * ThumbnailBroker::thumbnailQueueProcessHelper(void * ref)
-{
- ((ThumbnailBroker*)ref)->thumbnailQueueProcessTH();
- return NULL;
-}
-
-void ThumbnailBroker::FlushThumbQueue()
-{
- pthread_mutex_lock(&thumbnailQueueMutex);
- while(thumbnailComplete.size())
- {
- if(CheckThumbnailListener(thumbnailComplete.front().first))
- {
- thumbnailComplete.front().first.second->OnThumbnailReady(thumbnailComplete.front().second);
- }
- else
- {
-#ifdef DEBUG
- std::cout << typeid(*this).name() << " Listener lost, discarding request" << std::endl;
-#endif
- delete thumbnailComplete.front().second;
- }
- thumbnailComplete.pop_front();
- }
- pthread_mutex_unlock(&thumbnailQueueMutex);
-}
-
-void ThumbnailBroker::thumbnailQueueProcessTH()
-{
- time_t lastAction = time(NULL);
- pthread_mutex_lock(&runningMutex);
- thumbnailQueueRunning = true;
- pthread_mutex_unlock(&runningMutex);
- while(true)
- {
- //Shutdown after 2 seconds of idle
- if(time(NULL) - lastAction > 2)
- {
-#ifdef DEBUG
- std::cout << typeid(*this).name() << " Idle shutdown" << std::endl;
-#endif
- break;
- }
-
-
- pthread_mutex_lock(&runningMutex);
- bool running = thumbnailQueueRunning;
- pthread_mutex_unlock(&runningMutex);
- if(!running)
- {
-#ifdef DEBUG
- std::cout << typeid(*this).name() << " Requested shutdown" << std::endl;
-#endif
- break;
- }
-
-
- //Renderer
- pthread_mutex_lock(&thumbnailQueueMutex);
- if(renderRequests.size())
- {
- 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);
- }
- }
- else
- {
- pthread_mutex_unlock(&thumbnailQueueMutex);
- }
-
- //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
- }
- }
-
- 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);
- thumbnailQueueRunning = false;
- pthread_mutex_unlock(&runningMutex);
-}
-
-void ThumbnailBroker::RetrieveThumbnail(int saveID, int width, int height, ThumbnailListener * tListener)
-{
- RetrieveThumbnail(saveID, 0, width, height, tListener);
-}
-
-bool ThumbnailBroker::CheckThumbnailListener(ListenerHandle handle)
-{
- pthread_mutex_lock(&listenersMutex);
- int count = std::count(validListeners.begin(), validListeners.end(), handle);
- pthread_mutex_unlock(&listenersMutex);
-
- return count;
-}
-
-void ThumbnailBroker::AttachThumbnailListener(ThumbnailListener * tListener)
-{
- pthread_mutex_lock(&listenersMutex);
- validListeners.push_back(ListenerHandle(tListener->ListenerRand, tListener));
- pthread_mutex_unlock(&listenersMutex);
-}
-
-void ThumbnailBroker::DetachThumbnailListener(ThumbnailListener * tListener)
-{
- pthread_mutex_lock(&listenersMutex);
-
- std::vector<ListenerHandle>::iterator iter = validListeners.begin();
- while (iter != validListeners.end())
- {
- if(*iter == ListenerHandle(tListener->ListenerRand, tListener))
- iter = validListeners.erase(iter);
- else
- ++iter;
- }
-
- pthread_mutex_unlock(&listenersMutex);
-}
diff --git a/src/client/ThumbnailBroker.h b/src/client/ThumbnailBroker.h
deleted file mode 100644
index 5160479..0000000
--- a/src/client/ThumbnailBroker.h
+++ /dev/null
@@ -1,111 +0,0 @@
-#pragma once
-#include <queue>
-#include <list>
-#include <utility>
-#include <deque>
-#include <pthread.h>
-#undef GetUserName //God dammit microsoft!
-
-#include "Singleton.h"
-
-class GameSave;
-class Thumbnail;
-class ThumbnailListener;
-typedef std::pair<int, ThumbnailListener*> ListenerHandle;
-class ThumbnailBroker: public Singleton<ThumbnailBroker>
-{
-private:
- class ThumbnailSpec
- {
- public:
- int Width, Height;
- ListenerHandle CompletedListener;
- ThumbnailSpec(int width, int height, ListenerHandle completedListener) :
- Width(width), Height(height), CompletedListener(completedListener) {}
- };
-
- class ThumbnailID
- {
- public:
- int SaveID, SaveDate;
- bool operator ==(const ThumbnailID & second)
- {
- return SaveID == second.SaveID && SaveDate == second.SaveDate;
- }
- ThumbnailID(int saveID, int saveDate) : SaveID(saveID), SaveDate(saveDate) {}
- ThumbnailID() : SaveID(0), SaveDate(0) {}
- };
-
- class ThumbnailRequest
- {
- public:
- bool Complete;
- 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));
- }
- ThumbnailRequest() : Complete(false), HTTPContext(NULL), RequestTime(0) {}
- };
-
- class ThumbRenderRequest
- {
- public:
- int Width, Height;
- bool Decorations;
- bool Fire;
- GameSave * Save;
- ListenerHandle CompletedListener;
- ThumbRenderRequest(GameSave * save, bool decorations, bool fire, int width, int height, ListenerHandle completedListener) :
- 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)) {}
- };
-
- //Thumbnail retreival
- /*int thumbnailCacheNextID;
- Thumbnail * thumbnailCache[THUMB_CACHE_SIZE];
- void * activeThumbRequests[IMGCONNS];
- int activeThumbRequestTimes[IMGCONNS];
- int activeThumbRequestCompleteTimes[IMGCONNS];
- std::string activeThumbRequestIDs[IMGCONNS];*/
-
- pthread_mutex_t thumbnailQueueMutex;
- pthread_mutex_t listenersMutex;
- pthread_mutex_t runningMutex;
- pthread_t thumbnailQueueThread;
- 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;
-
- static void * thumbnailQueueProcessHelper(void * ref);
- void thumbnailQueueProcessTH();
- void assureRunning();
-
-public:
- ThumbnailBroker();
- virtual ~ThumbnailBroker();
- void Shutdown();
-
- void FlushThumbQueue();
- void RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, ThumbnailListener * tListener);
- void RenderThumbnail(GameSave * gameSave, int width, int height, ThumbnailListener * tListener);
- void RetrieveThumbnail(int saveID, int saveDate, int width, int height, ThumbnailListener * tListener);
- void RetrieveThumbnail(int saveID, int width, int height, ThumbnailListener * tListener);
-
- bool CheckThumbnailListener(ListenerHandle handle);
- void AttachThumbnailListener(ThumbnailListener * tListener);
- void DetachThumbnailListener(ThumbnailListener * tListener);
-}; \ No newline at end of file
diff --git a/src/client/ThumbnailListener.h b/src/client/ThumbnailListener.h
deleted file mode 100644
index 97bdef5..0000000
--- a/src/client/ThumbnailListener.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-class Thumbnail;
-class ThumbnailListener
-{
-public:
- int ListenerRand;
- ThumbnailListener() { ListenerRand = rand(); }
- virtual ~ThumbnailListener() {}
-
- virtual void OnThumbnailReady(Thumbnail * thumb) {}
-};
diff --git a/src/graphics/Graphics.cpp b/src/graphics/Graphics.cpp
index 068cc5d..3394ab2 100644
--- a/src/graphics/Graphics.cpp
+++ b/src/graphics/Graphics.cpp
@@ -35,10 +35,25 @@ VideoBuffer::VideoBuffer(VideoBuffer * old):
std::copy(old->Buffer, old->Buffer+(old->Width*old->Height), Buffer);
};
+VideoBuffer::VideoBuffer(pixel * buffer, int width, int height):
+ Width(width),
+ Height(height)
+{
+ Buffer = new pixel[width*height];
+ std::copy(buffer, buffer+(width*height), Buffer);
+}
+
void VideoBuffer::Resize(float factor, bool resample)
{
int newWidth = ((float)Width)*factor;
int newHeight = ((float)Height)*factor;
+ Resize(newWidth, newHeight);
+}
+
+void VideoBuffer::Resize(int width, int height, bool resample)
+{
+ int newWidth = width;
+ int newHeight = height;
pixel * newBuffer;
if(resample)
newBuffer = Graphics::resample_img(Buffer, Width, Height, newWidth, newHeight);
diff --git a/src/graphics/Graphics.h b/src/graphics/Graphics.h
index 2c90074..596d93d 100644
--- a/src/graphics/Graphics.h
+++ b/src/graphics/Graphics.h
@@ -110,8 +110,10 @@ public:
VideoBuffer(const VideoBuffer & old);
VideoBuffer(VideoBuffer * old);
+ VideoBuffer(pixel * buffer, int width, int height);
VideoBuffer(int width, int height);
void Resize(float factor, bool resample = false);
+ void Resize(int width, int height, bool resample = false);
TPT_INLINE void BlendPixel(int x, int y, int r, int g, int b, int a)
{
#ifdef PIX32OGL
diff --git a/src/interface/AvatarButton.cpp b/src/interface/AvatarButton.cpp
new file mode 100644
index 0000000..0a55ffa
--- /dev/null
+++ b/src/interface/AvatarButton.cpp
@@ -0,0 +1,117 @@
+#include <iostream>
+#include <typeinfo>
+
+#include "AvatarButton.h"
+#include "Format.h"
+#include "Engine.h"
+#include "client/Client.h"
+#include "client/RequestBroker.h"
+#include "graphics/Graphics.h"
+#include "ContextMenu.h"
+#include "Keys.h"
+
+namespace ui {
+
+AvatarButton::AvatarButton(Point position, Point size, std::string username):
+ Component(position, size),
+ name(username),
+ actionCallback(NULL),
+ avatar(NULL),
+ tried(false)
+{
+
+}
+
+AvatarButton::~AvatarButton()
+{
+ RequestBroker::Ref().DetachRequestListener(this);
+ if(avatar)
+ delete avatar;
+ if(actionCallback)
+ delete actionCallback;
+}
+
+void AvatarButton::Tick(float dt)
+{
+ if(!avatar && !tried && name.size() > 0)
+ {
+ tried = true;
+ RequestBroker::Ref().RetrieveAvatar(name, Size.X, Size.Y, this);
+ }
+}
+
+void AvatarButton::OnResponseReady(void * imagePtr)
+{
+ VideoBuffer * image = (VideoBuffer*)imagePtr;
+ if(image)
+ {
+ if(avatar)
+ delete avatar;
+ avatar = image;
+ }
+}
+
+void AvatarButton::Draw(const Point& screenPos)
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ if(avatar)
+ {
+ g->draw_image(avatar, screenPos.X, screenPos.Y, 255);
+ }
+}
+
+void AvatarButton::OnMouseUnclick(int x, int y, unsigned int button)
+{
+ if(button != 1)
+ {
+ return; //left click only!
+ }
+
+ if(isButtonDown)
+ {
+ isButtonDown = false;
+ DoAction();
+ }
+}
+
+void AvatarButton::OnContextMenuAction(int item)
+{
+ //Do nothing
+}
+
+void AvatarButton::OnMouseClick(int x, int y, unsigned int button)
+{
+ if(button == BUTTON_RIGHT)
+ {
+ if(menu)
+ menu->Show(GetScreenPos() + ui::Point(x, y));
+ }
+ else
+ {
+ isButtonDown = true;
+ }
+}
+
+void AvatarButton::OnMouseEnter(int x, int y)
+{
+ isMouseInside = true;
+}
+
+void AvatarButton::OnMouseLeave(int x, int y)
+{
+ isMouseInside = false;
+}
+
+void AvatarButton::DoAction()
+{
+ if(actionCallback)
+ actionCallback->ActionCallback(this);
+}
+
+void AvatarButton::SetActionCallback(AvatarButtonAction * action)
+{
+ actionCallback = action;
+}
+
+} /* namespace ui */
diff --git a/src/interface/AvatarButton.h b/src/interface/AvatarButton.h
new file mode 100644
index 0000000..d946ddf
--- /dev/null
+++ b/src/interface/AvatarButton.h
@@ -0,0 +1,54 @@
+#ifndef AVATARBUTTON_H_
+#define AVATARBUTTON_H_
+
+#include <string>
+
+#include "Component.h"
+#include "graphics/Graphics.h"
+#include "interface/Colour.h"
+#include "client/RequestListener.h"
+
+namespace ui
+{
+class AvatarButton;
+class AvatarButtonAction
+{
+public:
+ virtual void ActionCallback(ui::AvatarButton * sender) {}
+ virtual ~AvatarButtonAction() {}
+};
+
+class AvatarButton : public Component, public RequestListener
+{
+ VideoBuffer * avatar;
+ std::string name;
+ bool tried;
+public:
+ AvatarButton(Point position, Point size, std::string username);
+ virtual ~AvatarButton();
+
+ virtual void OnMouseClick(int x, int y, unsigned int button);
+ virtual void OnMouseUnclick(int x, int y, unsigned int button);
+
+ virtual void OnMouseEnter(int x, int y);
+ virtual void OnMouseLeave(int x, int y);
+
+ virtual void OnContextMenuAction(int item);
+
+ virtual void Draw(const Point& screenPos);
+ virtual void Tick(float dt);
+
+ virtual void OnResponseReady(void * imagePtr);
+
+ virtual void DoAction();
+
+ void SetUsername(std::string username) { name = username; }
+ std::string GetUsername() { return name; }
+ void SetActionCallback(AvatarButtonAction * action);
+protected:
+ bool isMouseInside, isButtonDown;
+ AvatarButtonAction * actionCallback;
+};
+}
+#endif /* AVATARBUTTON_H_ */
+
diff --git a/src/interface/SaveButton.cpp b/src/interface/SaveButton.cpp
index c5840c8..87b80a6 100644
--- a/src/interface/SaveButton.cpp
+++ b/src/interface/SaveButton.cpp
@@ -5,7 +5,7 @@
#include "client/SaveInfo.h"
#include "graphics/Graphics.h"
#include "Engine.h"
-#include "client/ThumbnailBroker.h"
+#include "client/RequestBroker.h"
#include "simulation/SaveRenderer.h"
#include "Format.h"
#include "ContextMenu.h"
@@ -117,7 +117,7 @@ SaveButton::SaveButton(Point position, Point size, SaveFile * file):
SaveButton::~SaveButton()
{
- ThumbnailBroker::Ref().DetachThumbnailListener(this);
+ RequestBroker::Ref().DetachRequestListener(this);
if(thumbnail)
delete thumbnail;
@@ -129,13 +129,14 @@ SaveButton::~SaveButton()
delete file;
}
-void SaveButton::OnThumbnailReady(Thumbnail * thumb)
+void SaveButton::OnResponseReady(void * imagePtr)
{
- if(thumb)
+ VideoBuffer * image = (VideoBuffer*)imagePtr;
+ if(image)
{
if(thumbnail)
delete thumbnail;
- thumbnail = thumb;
+ thumbnail = image;
waitingForThumb = false;
}
}
@@ -144,23 +145,25 @@ void SaveButton::Tick(float dt)
{
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->GetGameSave())
{
waitingForThumb = true;
- ThumbnailBroker::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())
{
waitingForThumb = true;
- ThumbnailBroker::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())
{
waitingForThumb = true;
- ThumbnailBroker::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)
{
- thumbBoxSize = ui::Point(thumbnail->Size.X, thumbnail->Size.Y);
+ thumbBoxSize = ui::Point(thumbnail->Width, thumbnail->Height);
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
- 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
{
diff --git a/src/interface/SaveButton.h b/src/interface/SaveButton.h
index 7a75973..e03d48a 100644
--- a/src/interface/SaveButton.h
+++ b/src/interface/SaveButton.h
@@ -6,9 +6,8 @@
#include "Component.h"
#include "client/SaveFile.h"
#include "client/SaveInfo.h"
-#include "client/ThumbnailListener.h"
+#include "client/RequestListener.h"
#include "graphics/Graphics.h"
-#include "search/Thumbnail.h"
#include "interface/Colour.h"
namespace ui
@@ -24,11 +23,11 @@ public:
virtual ~SaveButtonAction() {}
};
-class SaveButton : public Component, public ThumbnailListener
+class SaveButton : public Component, public RequestListener
{
SaveFile * file;
SaveInfo * save;
- Thumbnail * thumbnail;
+ VideoBuffer * thumbnail;
std::string name;
std::string votesString;
std::string votesBackground;
@@ -59,7 +58,7 @@ public:
virtual void Draw(const Point& screenPos);
virtual void Tick(float dt);
- virtual void OnThumbnailReady(Thumbnail * thumb);
+ virtual void OnResponseReady(void * imagePtr);
void SetSelected(bool selected_) { selected = selected_; }
bool GetSelected() { return selected; }
diff --git a/src/preview/PreviewView.cpp b/src/preview/PreviewView.cpp
index 005cef9..5b9ea9b 100644
--- a/src/preview/PreviewView.cpp
+++ b/src/preview/PreviewView.cpp
@@ -12,6 +12,7 @@
#include "search/Thumbnail.h"
#include "client/Client.h"
#include "interface/ScrollPanel.h"
+#include "interface/AvatarButton.h"
#include "interface/Keys.h"
class PreviewView::LoginAction: public ui::ButtonAction
@@ -52,7 +53,8 @@ PreviewView::PreviewView():
doOpen(false),
addCommentBox(NULL),
submitCommentButton(NULL),
- commentBoxHeight(20)
+ commentBoxHeight(20),
+ showAvatars(true)
{
class FavAction: public ui::ButtonAction
{
@@ -133,23 +135,38 @@ PreviewView::PreviewView():
browserOpenButton->SetActionCallback(new BrowserOpenAction(this));
AddComponent(browserOpenButton);
- saveNameLabel = new ui::Label(ui::Point(5, (YRES/2)+4), ui::Point(100, 16), "");
+ if(showAvatars)
+ saveNameLabel = new ui::Label(ui::Point(39, (YRES/2)+4), ui::Point(100, 16), "");
+ else
+ saveNameLabel = new ui::Label(ui::Point(5, (YRES/2)+4), ui::Point(100, 16), "");
saveNameLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
saveNameLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(saveNameLabel);
- saveDescriptionLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15+17), ui::Point((XRES/2)-10, Size.Y-((YRES/2)+4+15+17)-21), "");
+ if(showAvatars)
+ saveDescriptionLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15+21), ui::Point((XRES/2)-10, Size.Y-((YRES/2)+4+15+17)-25), "");
+ else
+ saveDescriptionLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15+19), ui::Point((XRES/2)-10, Size.Y-((YRES/2)+4+15+17)-23), "");
saveDescriptionLabel->SetMultiline(true);
saveDescriptionLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
saveDescriptionLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
saveDescriptionLabel->SetTextColour(ui::Colour(180, 180, 180));
AddComponent(saveDescriptionLabel);
- authorDateLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15), ui::Point(200, 16), "");
+ if(showAvatars)
+ authorDateLabel = new ui::Label(ui::Point(39, (YRES/2)+4+15), ui::Point(180, 16), "");
+ else
+ authorDateLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15), ui::Point(200, 16), "");
authorDateLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
authorDateLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(authorDateLabel);
+ if(showAvatars)
+ {
+ avatarButton = new ui::AvatarButton(ui::Point(4, (YRES/2)+4), ui::Point(34, 34), "");
+ AddComponent(avatarButton);
+ }
+
viewsLabel = new ui::Label(ui::Point((XRES/2)-80, (YRES/2)+4+15), ui::Point(80, 16), "");
viewsLabel->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
viewsLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
@@ -373,7 +390,14 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
votesUp = save->votesUp;
votesDown = save->votesDown;
saveNameLabel->SetText(save->name);
- authorDateLabel->SetText("\bgAuthor:\bw " + save->userName + " \bgDate:\bw " + format::UnixtimeToDateMini(save->date));
+ if(showAvatars) {
+ avatarButton->SetUsername(save->userName);
+ authorDateLabel->SetText("\bw" + save->userName + " \bgDate:\bw " + format::UnixtimeToDateMini(save->date));
+ }
+ else
+ {
+ authorDateLabel->SetText("\bgAuthor:\bw " + save->userName + " \bgDate:\bw " + format::UnixtimeToDateMini(save->date));
+ }
viewsLabel->SetText("\bgViews:\bw " + format::NumberToString<int>(save->Views));
saveDescriptionLabel->SetText(save->Description);
if(save->Favourite)
@@ -513,10 +537,21 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
int currentY = 0;//-yOffset;
ui::Label * tempUsername;
ui::Label * tempComment;
+ ui::AvatarButton * tempAvatar;
for(int i = 0; i < comments->size(); i++)
{
int usernameY = currentY+5, commentY;
- tempUsername = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorName);
+ if(showAvatars)
+ {
+ tempAvatar = new ui::AvatarButton(ui::Point(2, currentY+7), ui::Point(26, 26), comments->at(i)->authorName);
+ commentComponents.push_back(tempAvatar);
+ commentsPanel->AddChild(tempAvatar);
+ }
+
+ if(showAvatars)
+ tempUsername = new ui::Label(ui::Point(31, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorName);
+ else
+ 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.VerticalAlign = ui::Appearance::AlignBottom;
currentY += 16;
@@ -524,9 +559,11 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
commentComponents.push_back(tempUsername);
commentsPanel->AddChild(tempUsername);
-
commentY = currentY+5;
- tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), -1), comments->at(i)->comment);
+ 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->SetMultiline(true);
tempComment->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
tempComment->Appearance.VerticalAlign = ui::Appearance::AlignTop;
diff --git a/src/preview/PreviewView.h b/src/preview/PreviewView.h
index 03a3a47..1a01f9b 100644
--- a/src/preview/PreviewView.h
+++ b/src/preview/PreviewView.h
@@ -14,6 +14,7 @@
namespace ui
{
class ScrollPanel;
+ class AvatarButton;
}
class PreviewModel;
@@ -32,6 +33,7 @@ class PreviewView: public ui::Window {
ui::Textbox * addCommentBox;
ui::Label * saveNameLabel;
ui::Label * authorDateLabel;
+ ui::AvatarButton * avatarButton;
ui::Label * pageInfo;
ui::Label * saveDescriptionLabel;
ui::Label * viewsLabel;
@@ -42,6 +44,7 @@ class PreviewView: public ui::Window {
int votesUp;
int votesDown;
bool doOpen;
+ bool showAvatars;
int commentBoxHeight;
float commentBoxPositionX;
diff --git a/src/save/LocalSaveActivity.cpp b/src/save/LocalSaveActivity.cpp
index 96296f0..58c9a63 100644
--- a/src/save/LocalSaveActivity.cpp
+++ b/src/save/LocalSaveActivity.cpp
@@ -3,7 +3,7 @@
#include "interface/Textbox.h"
#include "interface/Button.h"
#include "search/Thumbnail.h"
-#include "client/ThumbnailBroker.h"
+#include "client/RequestBroker.h"
#include "dialogues/ErrorMessage.h"
#include "dialogues/ConfirmPrompt.h"
#include "client/Client.h"
@@ -67,7 +67,7 @@ LocalSaveActivity::LocalSaveActivity(SaveFile save, FileSavedCallback * callback
SetOkayButton(okayButton);
if(save.GetGameSave())
- ThumbnailBroker::Ref().RenderThumbnail(save.GetGameSave(), true, false, Size.X-16, -1, this);
+ RequestBroker::Ref().RenderThumbnail(save.GetGameSave(), true, false, Size.X-16, -1, this);
}
void LocalSaveActivity::Save()
@@ -123,14 +123,14 @@ void LocalSaveActivity::OnDraw()
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->drawrect(Position.X+(Size.X-thumbnail->Size.X)/2, Position.Y+45, thumbnail->Size.X, thumbnail->Size.Y, 180, 180, 180, 255);
+ g->draw_image(thumbnail, Position.X+(Size.X-thumbnail->Width)/2, Position.Y+45, 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()
diff --git a/src/save/LocalSaveActivity.h b/src/save/LocalSaveActivity.h
index aacbc0b..dd33fc4 100644
--- a/src/save/LocalSaveActivity.h
+++ b/src/save/LocalSaveActivity.h
@@ -2,14 +2,14 @@
#include "Activity.h"
#include "client/SaveFile.h"
-#include "client/ThumbnailListener.h"
+#include "client/RequestListener.h"
namespace ui
{
class Textbox;
}
-class Thumbnail;
+class VideoBuffer;
class FileSavedCallback
{
@@ -19,10 +19,10 @@ public:
virtual void FileSaved(SaveFile * file) {}
};
-class LocalSaveActivity: public WindowActivity, public ThumbnailListener
+class LocalSaveActivity: public WindowActivity, public RequestListener
{
SaveFile save;
- Thumbnail * thumbnail;
+ VideoBuffer * thumbnail;
ui::Textbox * filenameField;
class CancelAction;
class SaveAction;
@@ -34,6 +34,6 @@ public:
void saveWrite(std::string finalFilename);
virtual void Save();
virtual void OnDraw();
- virtual void OnThumbnailReady(Thumbnail * thumbnail);
+ virtual void OnRequestReady(void * imagePtr);
virtual ~LocalSaveActivity();
}; \ No newline at end of file
diff --git a/src/save/ServerSaveActivity.cpp b/src/save/ServerSaveActivity.cpp
index a666a73..1481b6d 100644
--- a/src/save/ServerSaveActivity.cpp
+++ b/src/save/ServerSaveActivity.cpp
@@ -3,8 +3,7 @@
#include "interface/Textbox.h"
#include "interface/Button.h"
#include "interface/Checkbox.h"
-#include "search/Thumbnail.h"
-#include "client/ThumbnailBroker.h"
+#include "client/RequestBroker.h"
#include "dialogues/ErrorMessage.h"
#include "dialogues/ConfirmPrompt.h"
#include "client/Client.h"
@@ -132,7 +131,7 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, ServerSaveActivity::SaveUp
SetOkayButton(okayButton);
if(save.GetGameSave())
- ThumbnailBroker::Ref().RenderThumbnail(save.GetGameSave(), false, true, (Size.X/2)-16, -1, this);
+ RequestBroker::Ref().RenderThumbnail(save.GetGameSave(), false, true, (Size.X/2)-16, -1, this);
}
ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, ServerSaveActivity::SaveUploadedCallback * callback) :
@@ -245,14 +244,14 @@ void ServerSaveActivity::OnDraw()
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->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->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->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()
diff --git a/src/save/ServerSaveActivity.h b/src/save/ServerSaveActivity.h
index 5f4cdfe..9e37748 100644
--- a/src/save/ServerSaveActivity.h
+++ b/src/save/ServerSaveActivity.h
@@ -2,7 +2,7 @@
#include "Activity.h"
#include "client/SaveInfo.h"
-#include "client/ThumbnailListener.h"
+#include "client/RequestListener.h"
#include "tasks/TaskListener.h"
namespace ui
@@ -13,7 +13,7 @@ namespace ui
class Task;
class Thumbnail;
-class ServerSaveActivity: public WindowActivity, public ThumbnailListener, public TaskListener
+class ServerSaveActivity: public WindowActivity, public RequestListener, public TaskListener
{
public:
class SaveUploadedCallback
@@ -29,7 +29,7 @@ public:
virtual void Save();
virtual void Exit();
virtual void OnDraw();
- virtual void OnThumbnailReady(Thumbnail * thumbnail);
+ virtual void OnRequestReady(void * imagePtr);
virtual void OnTick(float dt);
virtual ~ServerSaveActivity();
protected:
@@ -37,7 +37,7 @@ protected:
Task * saveUploadTask;
SaveUploadedCallback * callback;
SaveInfo save;
- Thumbnail * thumbnail;
+ VideoBuffer * thumbnail;
ui::Textbox * nameField;
ui::Textbox * descriptionField;
ui::Checkbox * publishedCheckbox;