Implemented a lazy load pattern for the deck stats - when the DeckMenu is displaying decks, it calls LoadStats() for only the ones visible in the list. This helps reduces the lag that occurs each time we attempt to load all the AI decks during match selection.

This still could be improved - DeckMetaData's constructor loads an MTGDeck object to parse out the name of a deck from its file.  This means that we crack open 106 files on the first attempt to show the list of opponent decks. I started optimizing this, but reverted, as the list itself is sorted alphabetically.  Currently, with these mods, it's still taking 4 1/2 seconds on my psp to load the opponent list on the first go around.

While at it, did some cleanup - removed the need for passing around a player pointer in some of the DeckStat functions, etc.
This commit is contained in:
wrenczes@gmail.com
2011-01-30 13:06:21 +00:00
parent ebc7f93682
commit 6675a7da31
16 changed files with 221 additions and 168 deletions
+1 -1
View File
@@ -117,7 +117,7 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app)
if (unlocked == -1)
{
DeckStats * stats = DeckStats::GetInstance();
stats->load(p1);
stats->load(p1->GetCurrentDeckStatsFile());
unlocked = isDifficultyUnlocked(stats);
if (unlocked)
{
+33 -1
View File
@@ -55,6 +55,38 @@ DeckMetaData* DeckManager::getDeckMetaDataById( int deckId, bool isAI )
return deck;
}
/*
** Predicate helper for getDeckMetadataByFilename()
*/
struct DeckFilenameMatch
{
DeckFilenameMatch(const std::string& filename) : mFilename(filename)
{
}
bool operator() (DeckMetaData* inPtr)
{
return inPtr->getFilename() == mFilename;
}
std::string mFilename;
};
DeckMetaData* DeckManager::getDeckMetaDataByFilename(const string& filename, bool isAI)
{
DeckMetaData* deck = NULL;
std::vector<DeckMetaData *>& deckList = isAI ? aiDeckOrderList : playerDeckOrderList;
std::vector<DeckMetaData *>::iterator pos = find_if(deckList.begin(), deckList.end(), DeckFilenameMatch(filename));
if (pos != deckList.end())
{
deck = *pos;
}
return deck;
}
StatsWrapper * DeckManager::getExtendedStatsForDeckId( int deckId, MTGAllCards *collection, bool isAI )
{
DeckMetaData *selectedDeck = getDeckMetaDataById( deckId, isAI );
@@ -133,7 +165,7 @@ int DeckManager::getDifficultyRating(Player *statsPlayer, Player *player)
{
DeckMetaDataList * metas = DeckMetaDataList::decksMetaData;
DeckMetaData *meta = metas->get(player->deckFile, statsPlayer);
DeckMetaData *meta = metas->get(player->deckFile);
return meta->getDifficulty();
}
+6
View File
@@ -184,6 +184,12 @@ void DeckMenu::Render()
DeckMenuItem *currentMenuItem = static_cast<DeckMenuItem*> (mObjects[i]);
if (currentMenuItem->mY - kLineHeight * startId < mY + height - kLineHeight + 7)
{
// only load stats for visible items in the list
if (currentMenuItem->meta)
{
currentMenuItem->meta->LoadStats();
}
if (currentMenuItem->hasFocus())
{
mSelectedDeckId = i;
+81 -70
View File
@@ -11,71 +11,74 @@
DeckMetaDataList * DeckMetaDataList::decksMetaData = NEW DeckMetaDataList();
DeckMetaData::DeckMetaData()
DeckMetaData::DeckMetaData(const string& filename)
: mFilename(filename), mGamesPlayed(0), mVictories(0), mPercentVictories(0), mDifficulty(0),
mDeckLoaded(false), mStatsLoaded(false)
{
// TODO, figure out how we can defer this to later - currently,
// there's a catch 22, as we sort the deck list alphabetically, so we need to open the deck file
// to get its name. This means that for the opponent list, we crack open 106 files just to read the deck name
//, which is the bulk of the remaining 4 second delay we see the first time we try to pick an opponent on the first match
LoadDeck();
}
DeckMetaData::DeckMetaData(string filename, Player * statsPlayer)
void DeckMetaData::LoadStats()
{
load(filename);
}
void DeckMetaData::loadStatsForPlayer(Player * statsPlayer, string deckStatsFileName)
{
DeckStats * stats = DeckStats::GetInstance();
_nbGamesPlayed = 0;
_percentVictories = 0;
_victories = 0;
if (statsPlayer)
if (!mStatsLoaded)
{
stats->load(statsPlayer);
DeckStat * opponentDeckStats = stats->getDeckStat(deckStatsFileName);
if (opponentDeckStats)
DeckStats * stats = DeckStats::GetInstance();
if (mIsAI)
{
_percentVictories = stats->percentVictories(deckStatsFileName);
_victories = opponentDeckStats->victories;
_nbGamesPlayed = opponentDeckStats->nbgames;
ostringstream oss;
int deckFilenameOffset = deckStatsFileName.find("deck") + 4;
int oppDeckId = atoi(deckStatsFileName.substr(deckFilenameOffset, deckStatsFileName.find_last_of(".")).c_str());
int avatarId = getAvatarId(oppDeckId);
oss << "avatar" << avatarId << ".jpg";
_avatarFilename = oss.str();
if (_percentVictories < 34)
stats->load(mPlayerDeck);
DeckStat * opponentDeckStats = stats->getDeckStat(mStatsFilename);
if (opponentDeckStats)
{
_difficulty = HARD;
}
else if (_percentVictories < 55)
{
_difficulty = NORMAL;
mPercentVictories = stats->percentVictories(mStatsFilename);
mVictories = opponentDeckStats->victories;
mGamesPlayed = opponentDeckStats->nbgames;
ostringstream oss;
int deckFilenameOffset = mStatsFilename.find("deck") + 4;
int oppDeckId = atoi(mStatsFilename.substr(deckFilenameOffset, mStatsFilename.find_last_of(".")).c_str());
int avatarId = getAvatarId(oppDeckId);
oss << "avatar" << avatarId << ".jpg";
mAvatarFilename = oss.str();
if (mPercentVictories < 34)
{
mDifficulty = HARD;
}
else if (mPercentVictories < 55)
{
mDifficulty = NORMAL;
}
else
{
mDifficulty = EASY;
}
}
else
{
_difficulty = EASY;
ostringstream oss;
oss << "avatar" << getAvatarId(mDeckId) << ".jpg";
mAvatarFilename = oss.str();
}
}
else
{
ostringstream oss;
oss << "avatar" << getAvatarId(_deckid) << ".jpg";
_avatarFilename = oss.str();
if (fileExists(mStatsFilename.c_str()))
{
stats->load(mStatsFilename);
mGamesPlayed = stats->nbGames();
mPercentVictories = stats->percentVictories();
mVictories = static_cast<int>(mGamesPlayed * (mPercentVictories / 100.0f));
}
}
mStatsLoaded = true;
}
else
{
if (fileExists(deckStatsFileName.c_str()))
{
stats->load(deckStatsFileName.c_str());
_nbGamesPlayed = stats->nbGames();
_percentVictories = stats->percentVictories();
_victories = _nbGamesPlayed * (_percentVictories / 100.0f);
}
}
stats = NULL;
}
// since we only have 100 stock avatar images, we need to recylce the images for deck numbers > 99
// since we only have 100 stock avatar images, we need to recycle the images for deck numbers > 99
int DeckMetaData::getAvatarId(int deckId)
{
int avatarId = deckId % 100;
@@ -85,17 +88,19 @@ int DeckMetaData::getAvatarId(int deckId)
return avatarId;
}
void DeckMetaData::load(string filename)
void DeckMetaData::LoadDeck()
{
MTGDeck * mtgd = NEW MTGDeck(filename.c_str(), NULL, 1);
_name = trim(mtgd->meta_name);
_desc = trim(mtgd->meta_desc);
_deckid = atoi((filename.substr(filename.find("deck") + 4, filename.find(".txt"))).c_str());
_percentVictories = 0;
_nbGamesPlayed = 0;
_filename = filename;
_victories = 0;
delete (mtgd);
if (!mDeckLoaded)
{
MTGDeck deck(mFilename.c_str(), NULL, 1);
mName = trim(deck.meta_name);
mDescription = trim(deck.meta_desc);
mDeckId = atoi((mFilename.substr(mFilename.find("deck") + 4, mFilename.find(".txt"))).c_str());
mDeckLoaded = true;
}
}
DeckMetaDataList::~DeckMetaDataList()
@@ -117,14 +122,14 @@ void DeckMetaDataList::invalidate(string filename)
}
}
DeckMetaData * DeckMetaDataList::get(string filename, Player * statsPlayer)
DeckMetaData * DeckMetaDataList::get(string filename)
{
map<string, DeckMetaData *>::iterator it = values.find(filename);
if (it == values.end())
{
if (fileExists(filename.c_str()))
{
values[filename] = NEW DeckMetaData(filename, statsPlayer);
values[filename] = NEW DeckMetaData(filename);
}
}
@@ -135,48 +140,48 @@ DeckMetaData * DeckMetaDataList::get(string filename, Player * statsPlayer)
string DeckMetaData::getFilename()
{
return _filename;
return mFilename;
}
string DeckMetaData::getName()
{
return _name;
return mName;
}
int DeckMetaData::getDeckId()
{
return _deckid;
return mDeckId;
}
string DeckMetaData::getAvatarFilename()
{
return _avatarFilename;
return mAvatarFilename;
}
int DeckMetaData::getGamesPlayed()
{
return _nbGamesPlayed;
return mGamesPlayed;
}
int DeckMetaData::getVictories()
{
return _victories;
return mVictories;
}
int DeckMetaData::getVictoryPercentage()
{
return _percentVictories;
return mPercentVictories;
}
int DeckMetaData::getDifficulty()
{
return _difficulty;
return mDifficulty;
}
string DeckMetaData::getDifficultyString()
{
string difficultyString = "Normal";
switch (_difficulty)
switch (mDifficulty)
{
case HARD:
difficultyString = "Hard";
@@ -191,16 +196,22 @@ string DeckMetaData::getDifficultyString()
string DeckMetaData::getDescription()
{
return _desc;
return mDescription;
}
string DeckMetaData::getStatsSummary()
{
LoadStats();
ostringstream statsSummary;
statsSummary << "Difficulty: " << getDifficultyString() << endl
<< "Victory %: " << getVictoryPercentage() << endl
<< "Games Played: " << getGamesPlayed() << endl;
return statsSummary.str();
}
void DeckMetaData::Invalidate()
{
mStatsLoaded = false;
}
+18 -33
View File
@@ -1,5 +1,6 @@
#include "PrecompiledHeader.h"
#include "DeckManager.h"
#include "DeckStats.h"
#include "Player.h"
#include "GameObserver.h"
@@ -27,19 +28,6 @@ DeckStats * DeckStats::GetInstance()
return mInstance;
}
void DeckStats::cleanStats()
{
/* map<string, DeckStat *>::iterator it;
for (it = stats.begin(); it != stats.end(); it++)
{
SAFE_DELETE(it->second);
}
stats.clear();
*/
}
DeckStats::~DeckStats()
{
map<string, map<string,DeckStat*> > ::iterator it;
@@ -118,14 +106,7 @@ int DeckStats::percentVictories()
return 50;
}
void DeckStats::load(Player * player)
{
char filename[512];
sprintf(filename, "stats/%s.txt", player->deckFileSmall.c_str());
load(options.profileFile(filename).c_str());
}
void DeckStats::load(const char * filename)
void DeckStats::load(const std::string& filename)
{
currentDeck = filename;
@@ -133,7 +114,7 @@ void DeckStats::load(const char * filename)
{
return;
}
wagic::ifstream file(filename);
wagic::ifstream file(filename.c_str());
std::string s;
if (file)
@@ -156,16 +137,9 @@ void DeckStats::load(const char * filename)
}
}
void DeckStats::save(Player * player)
void DeckStats::save(const std::string& filename)
{
char filename[512];
sprintf(filename, "stats/%s.txt", player->deckFileSmall.c_str());
save(options.profileFile(filename).c_str());
}
void DeckStats::save(const char * filename)
{
std::ofstream file(filename);
std::ofstream file(filename.c_str());
char writer[512];
if (file)
{
@@ -196,7 +170,7 @@ void DeckStats::saveStats(Player *player, Player *opponent, GameObserver * game)
{
victory = 0;
}
load(player);
load(currentDeck);
map<string, DeckStat *> *stats = &masterDeckStats[currentDeck];
map<string, DeckStat *>::iterator it = stats->find(opponent->deckFileSmall);
if (it == stats->end())
@@ -208,7 +182,18 @@ void DeckStats::saveStats(Player *player, Player *opponent, GameObserver * game)
it->second->victories += victory;
it->second->nbgames += 1;
}
save(player);
save(currentDeck);
DeckMetaData* playerMeta = DeckManager::GetInstance()->getDeckMetaDataByFilename(player->deckFile, false);
// metadata caches its internal data (number of games, victories, etc)
// tell it to refresh when stats are updated
if (playerMeta)
playerMeta->Invalidate();
DeckMetaData* aiMeta = DeckManager::GetInstance()->getDeckMetaDataByFilename(opponent->deckFile, true);
if (aiMeta)
aiMeta->Invalidate();
}
void DeckStats::EndInstance()
+14 -8
View File
@@ -17,7 +17,7 @@ vector<DeckMetaData *> GameState::fillDeckMenu(SimpleMenu * _menu, const string&
Player * statsPlayer)
{
vector<DeckMetaData *> deckMetaDataVector = getValidDeckMetaData(path, smallDeckPrefix, statsPlayer);
vector<DeckMetaData *> deckMetaDataVector = BuildDeckList(path, smallDeckPrefix, statsPlayer);
renderDeckMenu(_menu, deckMetaDataVector);
return deckMetaDataVector;
@@ -27,13 +27,13 @@ vector<DeckMetaData *> GameState::fillDeckMenu(DeckMenu * _menu, const string& p
Player * statsPlayer, int maxDecks)
{
vector<DeckMetaData *> deckMetaDataVector = getValidDeckMetaData(path, smallDeckPrefix, statsPlayer, maxDecks);
vector<DeckMetaData *> deckMetaDataVector = BuildDeckList(path, smallDeckPrefix, statsPlayer, maxDecks);
renderDeckMenu(_menu, deckMetaDataVector);
return deckMetaDataVector;
}
vector<DeckMetaData *> GameState::getValidDeckMetaData(const string& path, const string& smallDeckPrefix, Player * statsPlayer, int maxDecks)
vector<DeckMetaData *> GameState::BuildDeckList(const string& path, const string& smallDeckPrefix, Player * statsPlayer, int maxDecks)
{
vector<DeckMetaData*> retList;
@@ -45,8 +45,8 @@ vector<DeckMetaData *> GameState::getValidDeckMetaData(const string& path, const
found = 0;
std::ostringstream filename;
filename << path << "/deck" << nbDecks << ".txt";
DeckMetaData * meta = metas->get(filename.str(), statsPlayer);
string deckStatsFileName;
DeckMetaData * meta = metas->get(filename.str());
if (meta)
{
found = 1;
@@ -54,16 +54,22 @@ vector<DeckMetaData *> GameState::getValidDeckMetaData(const string& path, const
{
std::ostringstream aiStatsDeckName;
aiStatsDeckName << smallDeckPrefix << "_deck" << nbDecks;
deckStatsFileName = aiStatsDeckName.str();
meta->mStatsFilename = aiStatsDeckName.str();
meta->mIsAI = true;
if (meta->mPlayerDeck != statsPlayer->GetCurrentDeckStatsFile())
{
meta->mPlayerDeck = statsPlayer->GetCurrentDeckStatsFile();
meta->Invalidate();
}
}
else
{
std::ostringstream playerStatsDeckName;
playerStatsDeckName << "stats/player_deck" << nbDecks << ".txt";
deckStatsFileName = options.profileFile(playerStatsDeckName.str());
meta->mStatsFilename = options.profileFile(playerStatsDeckName.str());
meta->mIsAI = false;
}
meta->loadStatsForPlayer(statsPlayer, deckStatsFileName);
retList.push_back(meta);
nbDecks++;
}
+1 -6
View File
@@ -39,8 +39,6 @@ GameStateDeckViewer::GameStateDeckViewer(GameApp* parent) :
GameState(parent)
{
bgMusic = NULL;
nbDecks = 0;
deckNum = 0;
useFilter = 0;
isAIDeckSave = false;
mSwitching = false;
@@ -168,9 +166,7 @@ void GameStateDeckViewer::updateDecks()
DeckManager * deckManager = DeckManager::GetInstance();
vector<DeckMetaData *> playerDeckList = fillDeckMenu(welcome_menu, options.profileFile());
deckNum = 0;
newDeckname = "";
nbDecks = playerDeckList.size() + 1;
welcome_menu->Add(MENU_ITEM_NEW_DECK, "--NEW--");
if (options[Options::CHEATMODE].number && (!myCollection || myCollection->getCount(WSrcDeck::UNFILTERED_MIN_COPIES) < 4)) welcome_menu->Add(
MENU_ITEM_CHEAT_MODE, "--UNLOCK CARDS--");
@@ -319,7 +315,7 @@ void GameStateDeckViewer::saveDeck()
void GameStateDeckViewer::saveAsAIDeck(string deckName)
{
vector<DeckMetaData *> aiDecks = GameState::getValidDeckMetaData(JGE_GET_RES("ai/baka"), "ai_baka", NULL);
vector<DeckMetaData *> aiDecks = GameState::BuildDeckList(JGE_GET_RES("ai/baka"), "ai_baka", NULL);
int nbAiDecks = aiDecks.size() + 1;
aiDecks.clear();
@@ -1570,7 +1566,6 @@ void GameStateDeckViewer::ButtonPressed(int controllerId, int controlId)
loadDeck(deckIdNumber);
mStage = STAGE_WAITING;
deckNum = controlId;
break;
case MENU_DECK_BUILDER: //Save / exit menu
+1 -1
View File
@@ -111,7 +111,7 @@ void GameStateDuel::Start()
GameStateDuel::selectedPlayerDeckId, true);
deckmenu->enableDisplayDetailsOverride();
DeckManager *deckManager = DeckManager::GetInstance();
vector<DeckMetaData *> playerDeckList = getValidDeckMetaData(options.profileFile());
vector<DeckMetaData *> playerDeckList = BuildDeckList(options.profileFile());
int nbDecks = playerDeckList.size();
if (nbDecks)
+3 -1
View File
@@ -192,11 +192,13 @@ void GameStateMenu::fillScroller()
char buff2[512];
DeckStats * stats = DeckStats::GetInstance();
vector<DeckMetaData *> playerDecks = getValidDeckMetaData(options.profileFile(), "", NULL, 6);
vector<DeckMetaData *> playerDecks = BuildDeckList(options.profileFile(), "", NULL, 6);
int totalGames = 0;
for (size_t j = 0; j < playerDecks.size(); j++)
{
DeckMetaData* meta = playerDecks[j];
if (meta)
meta->LoadStats();
sprintf(buffer, "stats/player_deck%i.txt", meta->getDeckId());
string deckstats = options.profileFile(buffer);
if (fileExists(deckstats.c_str()))
+24 -15
View File
@@ -134,7 +134,7 @@ int Player::gainOrLoseLife(int value)
game->receiveEvent(lifed);
return value;
};
}
int Player::gainLife(int value)
{
@@ -144,7 +144,7 @@ int Player::gainLife(int value)
return 0;
}
return gainOrLoseLife(value);
};
}
int Player::loseLife(int value)
{
@@ -154,21 +154,23 @@ int Player::loseLife(int value)
return 0;
}
return gainOrLoseLife(-value);
};
}
int Player::afterDamage()
{
return life;
}
int Player::poisoned()
{
return poisonCount;
}
int Player::damaged()
{
return damageCount;
}
int Player::prevented()
{
return preventable;
@@ -176,17 +178,17 @@ int Player::prevented()
void Player::takeMulligan()
{
MTGPlayerCards * currentPlayerZones = game;
int cardsinhand = currentPlayerZones->hand->nb_cards;
for (int i = 0; i < cardsinhand; i++) //Discard hand
currentPlayerZones->putInZone(currentPlayerZones->hand->cards[0],
currentPlayerZones->hand,
currentPlayerZones->library);
currentPlayerZones->library->shuffle(); //Shuffle
for (int i = 0; i < (cardsinhand - 1); i++)
game->drawFromLibrary();
MTGPlayerCards * currentPlayerZones = game;
int cardsinhand = currentPlayerZones->hand->nb_cards;
for (int i = 0; i < cardsinhand; i++) //Discard hand
currentPlayerZones->putInZone(currentPlayerZones->hand->cards[0],
currentPlayerZones->hand,
currentPlayerZones->library);
currentPlayerZones->library->shuffle(); //Shuffle
for (int i = 0; i < (cardsinhand - 1); i++)
game->drawFromLibrary();
//Draw hand with 1 less card penalty //almhum
}
@@ -197,6 +199,13 @@ void Player::cleanupPhase()
game->graveyard->cleanupPhase();
}
std::string Player::GetCurrentDeckStatsFile()
{
std::ostringstream filename;
filename << "stats/" << deckFileSmall << ".txt";
return options.profileFile(filename.str());
}
ostream& operator<<(ostream& out, const Player& p)
{
return out << p.getDisplayName();