2013-07-18 17:18:40 -04:00
|
|
|
//
|
|
|
|
// SuperTuxKart - a fun racing game with go-kart
|
2013-07-28 10:11:20 -04:00
|
|
|
// Copyright (C) 2010 Lucas Baudin
|
|
|
|
// 2011 Joerg Henrichs
|
|
|
|
// 2013 Glenn De Jonghe
|
2013-07-18 17:18:40 -04:00
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License
|
|
|
|
// as published by the Free Software Foundation; either version 3
|
|
|
|
// of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
#include "online/http_manager.hpp"
|
|
|
|
|
2013-08-18 18:22:00 -04:00
|
|
|
#include "online/current_user.hpp"
|
2013-08-18 19:46:36 -04:00
|
|
|
#include "states_screens/state_manager.hpp"
|
2013-08-18 18:22:00 -04:00
|
|
|
|
2013-07-18 17:18:40 -04:00
|
|
|
#include <iostream>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <memory.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#if defined(WIN32) && !defined(__CYGWIN__)
|
|
|
|
# include <windows.h>
|
|
|
|
#else
|
|
|
|
# include <sys/time.h>
|
|
|
|
# include <math.h>
|
|
|
|
#endif
|
|
|
|
|
2013-08-18 18:22:00 -04:00
|
|
|
using namespace Online;
|
|
|
|
|
2013-07-19 17:20:32 -04:00
|
|
|
namespace Online{
|
2013-08-20 16:58:34 -04:00
|
|
|
#define MENU_POLLING_INTERVAL 10.0f
|
|
|
|
#define GAME_POLLING_INTERVAL 15.0f
|
2013-07-18 17:18:40 -04:00
|
|
|
|
|
|
|
static HTTPManager * http_singleton = NULL;
|
|
|
|
|
|
|
|
HTTPManager* HTTPManager::get()
|
|
|
|
{
|
|
|
|
if (http_singleton == NULL)
|
|
|
|
{
|
|
|
|
http_singleton = new HTTPManager();
|
|
|
|
}
|
|
|
|
return http_singleton;
|
|
|
|
} // get
|
|
|
|
|
|
|
|
void HTTPManager::deallocate()
|
|
|
|
{
|
2013-09-12 14:23:56 -04:00
|
|
|
if (http_singleton != NULL)
|
|
|
|
{
|
|
|
|
delete http_singleton;
|
|
|
|
http_singleton = NULL;
|
|
|
|
}
|
2013-07-18 17:18:40 -04:00
|
|
|
} // deallocate
|
|
|
|
|
2013-07-19 22:14:36 -04:00
|
|
|
bool HTTPManager::isRunning()
|
|
|
|
{
|
|
|
|
return http_singleton != NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-18 17:18:40 -04:00
|
|
|
|
|
|
|
HTTPManager::HTTPManager(){
|
|
|
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
|
|
pthread_cond_init(&m_cond_request, NULL);
|
2013-07-19 21:38:44 -04:00
|
|
|
m_abort.setAtomic(false);
|
2013-08-27 19:24:25 -04:00
|
|
|
m_time_since_poll = MENU_POLLING_INTERVAL * 0.9;
|
2013-07-18 17:18:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
HTTPManager::~HTTPManager(){
|
2013-09-12 14:23:56 -04:00
|
|
|
m_thread_id.lock();
|
|
|
|
pthread_join(*m_thread_id.getData(), NULL);
|
|
|
|
delete m_thread_id.getData();
|
|
|
|
m_thread_id.unlock();
|
|
|
|
pthread_cond_destroy(&m_cond_request);
|
2013-07-18 17:18:40 -04:00
|
|
|
curl_global_cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Start the actual network thread. This can not be done as part of
|
|
|
|
* the constructor, since the assignment to the global network_http
|
|
|
|
* variable has not been assigned at that stage, and the thread might
|
|
|
|
* use network_http - a very subtle race condition. So the thread can
|
|
|
|
* only be started after the assignment (in main) has been done.
|
|
|
|
*/
|
|
|
|
void HTTPManager::startNetworkThread()
|
|
|
|
{
|
|
|
|
pthread_attr_t attr;
|
|
|
|
pthread_attr_init(&attr);
|
|
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
|
|
|
// Should be the default, but just in case:
|
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
|
|
|
//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
|
|
|
|
|
|
|
m_thread_id.setAtomic(new pthread_t());
|
|
|
|
int error = pthread_create(m_thread_id.getData(), &attr,
|
|
|
|
&HTTPManager::mainLoop, this);
|
|
|
|
if(error)
|
|
|
|
{
|
|
|
|
m_thread_id.lock();
|
|
|
|
delete m_thread_id.getData();
|
|
|
|
m_thread_id.unlock();
|
|
|
|
m_thread_id.setAtomic(0);
|
|
|
|
Log::error("HTTP Manager", "Could not create thread, error=%d.\n", errno);
|
|
|
|
}
|
|
|
|
pthread_attr_destroy(&attr);
|
2013-09-12 14:23:56 -04:00
|
|
|
CurrentUser::get()->requestSavedSession();
|
2013-07-18 17:18:40 -04:00
|
|
|
} // startNetworkThread
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** This function inserts a high priority request to quit into the request
|
|
|
|
* queue of the network thead, and also aborts any ongoing download.
|
|
|
|
* Separating this allows more time for the thread to finish cleanly,
|
|
|
|
* before it gets cancelled in the destructor.
|
|
|
|
*/
|
|
|
|
void HTTPManager::stopNetworkThread()
|
|
|
|
{
|
|
|
|
// If a download should be active (which means it was cancelled by the
|
|
|
|
// user, in which case it will still be ongoing in the background)
|
|
|
|
// we can't get the mutex, and would have to wait for a timeout,
|
|
|
|
// and we couldn't finish STK. This way we request an abort of
|
|
|
|
// a download, which mean we can get the mutex and ask the service
|
|
|
|
// thread here to cancel properly.
|
2013-09-11 22:29:05 -04:00
|
|
|
//cancelAllDownloads(); FIXME if used this way it also cancels the client-quit action
|
2013-08-28 18:45:11 -04:00
|
|
|
CurrentUser::get()->onSTKQuit();
|
2013-12-03 18:54:11 -05:00
|
|
|
addRequest(new Request(true, HTTP_MAX_PRIORITY, Request::RT_QUIT));
|
2013-07-18 17:18:40 -04:00
|
|
|
} // stopNetworkThread
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/** Signals to the progress function to request any ongoing download to be
|
|
|
|
* cancelled. This function can also be called if there is actually no
|
|
|
|
* download atm. The function progressDownload checks m_abort and will
|
|
|
|
* return a non-zero value which causes libcurl to abort. */
|
|
|
|
void HTTPManager::cancelAllDownloads()
|
|
|
|
{
|
|
|
|
m_abort.setAtomic(true);
|
2013-12-03 18:54:11 -05:00
|
|
|
// FIXME doesn't get called at the moment. When using this again,
|
|
|
|
// be sure that HTTP_MAX_PRIORITY requests still get executed.
|
2013-07-18 17:18:40 -04:00
|
|
|
} // cancelAllDownloads
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/** Inserts a request into the queue of all requests. The request will be
|
|
|
|
* sorted by priority.
|
|
|
|
* \param request The pointer to the new request to insert.
|
|
|
|
*/
|
2013-07-20 20:47:48 -04:00
|
|
|
void HTTPManager::addRequest(Request *request)
|
2013-07-18 17:18:40 -04:00
|
|
|
{
|
2013-07-19 21:38:44 -04:00
|
|
|
assert(request->isAllowedToAdd());
|
2013-08-06 18:45:33 -04:00
|
|
|
request->setBusy();
|
2013-07-19 21:38:44 -04:00
|
|
|
m_request_queue.lock();
|
|
|
|
m_request_queue.getData().push(request);
|
|
|
|
// Wake up the network http thread
|
|
|
|
pthread_cond_signal(&m_cond_request);
|
|
|
|
m_request_queue.unlock();
|
2013-07-24 09:08:45 -04:00
|
|
|
} // addRequest
|
|
|
|
|
2013-08-18 17:08:27 -04:00
|
|
|
|
2013-07-24 09:08:45 -04:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/** Immediately performs a request synchronously
|
2013-09-15 15:30:50 -04:00
|
|
|
* \param request The pointer to the request to execute.
|
2013-07-24 09:08:45 -04:00
|
|
|
*/
|
|
|
|
void HTTPManager::synchronousRequest(Request *request)
|
|
|
|
{
|
|
|
|
assert(request->isAllowedToAdd());
|
2013-08-15 08:35:23 -04:00
|
|
|
request->setBusy();
|
2013-07-24 09:08:45 -04:00
|
|
|
request->execute();
|
2013-08-26 21:02:41 -04:00
|
|
|
request->callback();
|
|
|
|
request->setDone();
|
2013-07-24 09:08:45 -04:00
|
|
|
} // synchronousRequest
|
2013-07-18 17:18:40 -04:00
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** The actual main loop, which is started as a separate thread from the
|
|
|
|
* constructor. After testing for a new server, fetching news, the list
|
|
|
|
* of packages to download, it will wait for commands to be issued.
|
|
|
|
* \param obj: A pointer to this object, passed on by pthread_create
|
|
|
|
*/
|
|
|
|
void *HTTPManager::mainLoop(void *obj)
|
|
|
|
{
|
|
|
|
HTTPManager *me = (HTTPManager*) obj;
|
|
|
|
|
2013-12-01 23:27:55 -05:00
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
2013-07-18 17:18:40 -04:00
|
|
|
|
|
|
|
me->m_current_request = NULL;
|
|
|
|
me->m_request_queue.lock();
|
2013-07-21 11:21:48 -04:00
|
|
|
while( me->m_request_queue.getData().empty() || me->m_request_queue.getData().top()->getType() != Request::RT_QUIT)
|
2013-07-18 17:18:40 -04:00
|
|
|
{
|
|
|
|
bool empty = me->m_request_queue.getData().empty();
|
|
|
|
// Wait in cond_wait for a request to arrive. The 'while' is necessary
|
|
|
|
// since "spurious wakeups from the pthread_cond_wait ... may occur"
|
|
|
|
// (pthread_cond_wait man page)!
|
|
|
|
while(empty)
|
|
|
|
{
|
|
|
|
pthread_cond_wait(&me->m_cond_request, me->m_request_queue.getMutex());
|
|
|
|
empty = me->m_request_queue.getData().empty();
|
|
|
|
}
|
|
|
|
me->m_current_request = me->m_request_queue.getData().top();
|
|
|
|
me->m_request_queue.getData().pop();
|
|
|
|
me->m_request_queue.unlock();
|
|
|
|
me->m_current_request->execute();
|
2013-08-18 18:22:00 -04:00
|
|
|
me->addResult(me->m_current_request);
|
2013-07-18 17:18:40 -04:00
|
|
|
me->m_request_queue.lock();
|
|
|
|
} // while
|
|
|
|
|
|
|
|
// At this stage we have the lock for m_request_queue
|
|
|
|
while(!me->m_request_queue.getData().empty())
|
|
|
|
{
|
2013-07-19 17:20:32 -04:00
|
|
|
Online::Request * request = me->m_request_queue.getData().top();
|
2013-07-18 17:18:40 -04:00
|
|
|
me->m_request_queue.getData().pop();
|
|
|
|
// Manage memory can be ignored here, all requests
|
|
|
|
// need to be freed.
|
|
|
|
delete request;
|
|
|
|
}
|
|
|
|
me->m_request_queue.unlock();
|
|
|
|
pthread_exit(NULL);
|
|
|
|
return 0;
|
|
|
|
} // mainLoop
|
2013-08-18 18:22:00 -04:00
|
|
|
|
2013-09-15 15:30:50 -04:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/** Inserts a request into the queue of results.
|
|
|
|
* \param request The pointer to the request to insert.
|
|
|
|
*/
|
2013-08-18 18:22:00 -04:00
|
|
|
void HTTPManager::addResult(Online::Request *request)
|
|
|
|
{
|
|
|
|
assert(request->isBusy());
|
|
|
|
m_result_queue.lock();
|
|
|
|
m_result_queue.getData().push(request);
|
|
|
|
m_result_queue.unlock();
|
|
|
|
}
|
|
|
|
|
2013-09-15 15:30:50 -04:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/**
|
|
|
|
* Takes a request out of the result queue, if any is present.
|
|
|
|
* Calls the callback method of the request and takes care of memory management if necessary.
|
|
|
|
*/
|
2013-08-18 18:22:00 -04:00
|
|
|
void HTTPManager::handleResultQueue()
|
|
|
|
{
|
|
|
|
Request * request = NULL;
|
|
|
|
m_result_queue.lock();
|
|
|
|
if(!m_result_queue.getData().empty())
|
|
|
|
{
|
|
|
|
request = m_result_queue.getData().front();
|
|
|
|
m_result_queue.getData().pop();
|
|
|
|
}
|
|
|
|
m_result_queue.unlock();
|
|
|
|
if(request != NULL)
|
|
|
|
{
|
|
|
|
request->callback();
|
|
|
|
if(request->manageMemory())
|
|
|
|
{
|
|
|
|
delete request;
|
|
|
|
request = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
request->setDone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-15 15:30:50 -04:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/**
|
|
|
|
* Should be called every frame and takes care of processing the result queue
|
|
|
|
* and polling the database server if a user is signed in.
|
|
|
|
*/
|
2013-08-18 18:22:00 -04:00
|
|
|
void HTTPManager::update(float dt){
|
|
|
|
handleResultQueue();
|
|
|
|
|
|
|
|
//Database polling starts here, only needed for registered users
|
|
|
|
if(!CurrentUser::get()->isRegisteredUser())
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_time_since_poll += dt;
|
2013-08-18 19:46:36 -04:00
|
|
|
float interval = GAME_POLLING_INTERVAL;
|
|
|
|
if (StateManager::get()->getGameState() == GUIEngine::MENU)
|
|
|
|
interval = MENU_POLLING_INTERVAL;
|
2013-08-18 18:22:00 -04:00
|
|
|
if(m_time_since_poll > interval)
|
|
|
|
{
|
|
|
|
m_time_since_poll = 0;
|
|
|
|
CurrentUser::get()->requestPoll();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2013-07-19 17:20:32 -04:00
|
|
|
} // namespace Online
|
2013-07-18 17:18:40 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|