283 lines
9.4 KiB
C++
283 lines
9.4 KiB
C++
//
|
|
// SuperTuxKart - a fun racing game with go-kart
|
|
// Copyright (C) 2013 Glenn De Jonghe
|
|
//
|
|
// 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/profile_manager.hpp"
|
|
|
|
#include "online/online_profile.hpp"
|
|
#include "utils/log.hpp"
|
|
#include "utils/translation.hpp"
|
|
|
|
#include <sstream>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
using namespace Online;
|
|
|
|
namespace Online
|
|
{
|
|
|
|
ProfileManager* ProfileManager::m_profile_manager = NULL;
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Private constructor, used by static create() function.
|
|
*/
|
|
ProfileManager::ProfileManager()
|
|
{
|
|
m_max_cache_size = 2;
|
|
m_currently_visiting = NULL;
|
|
} // ProfileManager
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Destructor, which frees the persistent and cached data.
|
|
*/
|
|
ProfileManager::~ProfileManager()
|
|
{
|
|
clearPersistent();
|
|
ProfilesMap::iterator it;
|
|
for (it = m_profiles_cache.begin(); it != m_profiles_cache.end(); ++it)
|
|
{
|
|
delete it->second;
|
|
}
|
|
} // ~ProfileManager
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Makes sure that the cache can store at least max_num entries. This is
|
|
* used by the online search screen to make sure all results found can
|
|
* be cached at the same time.
|
|
* \param min_num Minimum number of entries the chache should be able to
|
|
* store.
|
|
*/
|
|
int ProfileManager::guaranteeCacheSize(unsigned int min_num)
|
|
{
|
|
if (m_max_cache_size < min_num)
|
|
{
|
|
// Avoid that the cache can grow too big by setting an
|
|
// upper limit.
|
|
if (min_num > 100)
|
|
min_num = 100;
|
|
m_max_cache_size = min_num;
|
|
}
|
|
|
|
return m_max_cache_size;
|
|
} // guaranteeCacheSize
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Search for a given profile in the set of persistent and cached
|
|
* entries. If the profile does not exist, a NULL is returned.
|
|
* FIXME: This should be improved to download the profile is necessary.
|
|
* \param id The id of the profile to find.
|
|
*/
|
|
OnlineProfile* ProfileManager::getProfileByID(const uint32_t id)
|
|
{
|
|
if (inPersistent(id))
|
|
return m_profiles_persistent[id];
|
|
if (isInCache(id))
|
|
return m_profiles_cache[id];
|
|
|
|
// FIXME not able to get! Now this should actually fetch the info from the
|
|
// server, but I haven't come up with a good asynchronous idea yet.
|
|
return NULL;
|
|
} // getProfileByID
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Adds profile to the cache. If the profile is already persistent, then
|
|
* it merges any new information from this profile to the persistent one.
|
|
* If the entry is already in the cache, the cached entry will be updated
|
|
* with any new information from the given profile. Otherwise, the profile
|
|
* is just added to the cache.
|
|
*/
|
|
void ProfileManager::addToCache(OnlineProfile * profile)
|
|
{
|
|
if (inPersistent(profile->getID()))
|
|
m_profiles_persistent[profile->getID()]->merge(profile);
|
|
else if (isInCache(profile->getID()))
|
|
m_profiles_cache[profile->getID()]->merge(profile);
|
|
else
|
|
addDirectToCache(profile);
|
|
} // addToCache
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Initialisation before the object is displayed. If necessary this function
|
|
* will pause the race if it is running (i.e. world exists). While only some
|
|
* of the screen can be shown during the race (via the in-game menu you
|
|
* can get the options screen and the help screens only). This is used by
|
|
* the RaceResultGUI to leave the race running (for the end animation) while
|
|
* the results are being shown.
|
|
*/
|
|
void ProfileManager::addDirectToCache(OnlineProfile* profile)
|
|
{
|
|
assert(profile != NULL);
|
|
if (m_profiles_cache.size() == m_max_cache_size)
|
|
{
|
|
// We have to replace a cached entry, find one entry that
|
|
// doesn't have its used bit set
|
|
ProfilesMap::iterator iter;
|
|
for (iter = m_profiles_cache.begin(); iter != m_profiles_cache.end();)
|
|
{
|
|
if (!iter->second->getCacheBit())
|
|
{
|
|
delete iter->second;
|
|
m_profiles_cache.erase(iter);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
++iter;
|
|
}
|
|
} // for profile in cache
|
|
}
|
|
|
|
m_profiles_cache[profile->getID()] = profile;
|
|
assert(m_profiles_cache.size() <= m_max_cache_size);
|
|
} // addDirectToCache
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Checks if a profile is in cache. If so, it updates its usage bit.
|
|
* \param id Identifier for the profile to check.
|
|
*/
|
|
bool ProfileManager::isInCache(const uint32_t id)
|
|
{
|
|
ProfilesMap::const_iterator i = m_profiles_cache.find(id);
|
|
if (i != m_profiles_cache.end())
|
|
{
|
|
updateCacheBits(i->second);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} // isInCache
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** This function updates the cache bits of all cached entries. It will
|
|
* set the cache bit of the given profile. Then, if the cachen is full
|
|
* it will check if there are any entries that don't have the cache bit
|
|
* set (i.e. entries that can be discarded because they were not used).
|
|
* If no such entry is found, all usage flags will be reset, and only
|
|
* the one for the given entry will remain set. This results with high
|
|
* probability that most often used entries will remain in the cache,
|
|
* without adding much overhead.
|
|
*/
|
|
void ProfileManager::updateCacheBits(OnlineProfile * profile)
|
|
{
|
|
profile->setCacheBit(true);
|
|
if (m_profiles_cache.size() == m_max_cache_size)
|
|
{
|
|
ProfilesMap::iterator iter;
|
|
for (iter = m_profiles_cache.begin();
|
|
iter != m_profiles_cache.end(); ++iter)
|
|
{
|
|
if (!iter->second->getCacheBit())
|
|
return;
|
|
}
|
|
|
|
// All cache bits are set! Set them all to zero except the one
|
|
// currently being visited
|
|
for (iter = m_profiles_cache.begin();
|
|
iter != m_profiles_cache.end(); ++iter)
|
|
{
|
|
iter->second->setCacheBit(false);
|
|
}
|
|
profile->setCacheBit(true);
|
|
}
|
|
} // updateCacheBits
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** True if the profile with the given id is persistent.
|
|
* \param id The id of the profile to test.
|
|
*/
|
|
bool ProfileManager::inPersistent(const uint32_t id)
|
|
{
|
|
return m_profiles_persistent.find(id) != m_profiles_persistent.end();
|
|
} // inPersistent
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Adds a profile to the persistent map. If a profile with the same id
|
|
* is already in there, the profiles are "merged" with the goal to save as
|
|
* much information (i.e. one profile instance could have already fetched
|
|
* the friends, while the other could have fetched the achievements.)
|
|
* \param profile The profile to make persistent.
|
|
*/
|
|
void ProfileManager::addPersistent(OnlineProfile * profile)
|
|
{
|
|
if (inPersistent(profile->getID()))
|
|
{
|
|
m_profiles_persistent[profile->getID()]->merge(profile);
|
|
}
|
|
else
|
|
{
|
|
m_profiles_persistent[profile->getID()] = profile;
|
|
}
|
|
} // addPersistent
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Removes and deletes an entry from the persistent map.
|
|
* \param id the id of the profile to be removed.
|
|
*/
|
|
void ProfileManager::deleteFromPersistent(const uint32_t id)
|
|
{
|
|
if (inPersistent(id))
|
|
{
|
|
delete m_profiles_persistent[id];
|
|
m_profiles_persistent.erase(id);
|
|
}
|
|
else
|
|
{
|
|
Log::warn("ProfileManager",
|
|
"Tried to remove profile with id %d from persistent while "
|
|
"not present", id);
|
|
}
|
|
} // deleteFromPersistent
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Deletes all persistent profiles.
|
|
*/
|
|
void ProfileManager::clearPersistent()
|
|
{
|
|
ProfilesMap::iterator it;
|
|
for (it = m_profiles_persistent.begin();
|
|
it != m_profiles_persistent.end(); ++it)
|
|
{
|
|
delete it->second;
|
|
}
|
|
m_profiles_persistent.clear();
|
|
} // clearPersistent
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Removes a currently persistent profile to the cache (where it can
|
|
* be deleted later).
|
|
* \param id The the id of the profile to be moved.
|
|
*/
|
|
void ProfileManager::moveToCache(const uint32_t id)
|
|
{
|
|
if (inPersistent(id))
|
|
{
|
|
OnlineProfile * profile = getProfileByID(id);
|
|
m_profiles_persistent.erase(id);
|
|
addToCache(profile);
|
|
}
|
|
else
|
|
{
|
|
Log::warn("ProfileManager",
|
|
"Tried to move profile with id %d from persistent to "
|
|
"cache while not present", id);
|
|
}
|
|
} // moveToCache
|
|
|
|
} // namespace Online
|