stk-code_catmod/src/online/profile_manager.cpp

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