mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-22 17:49:43 +01:00
Change levelstats data type from vector to map
As described in #1016, there used to be a bug that inflated levelstats.vvv in 2.3, which was fixed in 2.4, but there was no way for inflated files to get smaller yet. This commit changes the storage of levelstats from a std::vector of structs to a std::map, so that uniqueness is guaranteed and thus the stats can be optimized automatically. And it also simplifies *and* optimizes the code that handles the levelstats - no more big loops that iterated over every element to find the matching level. (Farewell to the "life optimisation and all that" comment, too) I tested this with both my own levelstats.vvv, as well as some inflated ones (including Balneor's 93 MB one) and saw this code correctly reduce the filesize and speed up the levels list. Fixes #1016.
This commit is contained in:
parent
82240d262b
commit
e0e902d717
2 changed files with 67 additions and 83 deletions
|
@ -423,29 +423,9 @@ void Game::updatecustomlevelstats(std::string clevel, int cscore)
|
||||||
{
|
{
|
||||||
clevel = clevel.substr(7);
|
clevel = clevel.substr(7);
|
||||||
}
|
}
|
||||||
int tvar=-1;
|
if (customlevelstats.count(clevel) == 0 || cscore > customlevelstats[clevel])
|
||||||
for(size_t j=0; j<customlevelstats.size(); j++)
|
|
||||||
{
|
{
|
||||||
if(clevel==customlevelstats[j].name)
|
customlevelstats[clevel] = cscore;
|
||||||
{
|
|
||||||
tvar=j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(tvar>=0)
|
|
||||||
{
|
|
||||||
// We have an existing entry
|
|
||||||
// Don't update it unless it's a higher score
|
|
||||||
if (cscore > customlevelstats[tvar].score)
|
|
||||||
{
|
|
||||||
customlevelstats[tvar].score=cscore;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//add a new entry
|
|
||||||
CustomLevelStat levelstat = {clevel, cscore};
|
|
||||||
customlevelstats.push_back(levelstat);
|
|
||||||
}
|
}
|
||||||
savecustomlevelstats();
|
savecustomlevelstats();
|
||||||
}
|
}
|
||||||
|
@ -526,21 +506,40 @@ void Game::loadcustomlevelstats(void)
|
||||||
|
|
||||||
if (SDL_strcmp(pKey, "stats") == 0)
|
if (SDL_strcmp(pKey, "stats") == 0)
|
||||||
{
|
{
|
||||||
|
bool file_has_duplicates = false;
|
||||||
|
|
||||||
for (tinyxml2::XMLElement* stat_el = pElem->FirstChildElement(); stat_el; stat_el = stat_el->NextSiblingElement())
|
for (tinyxml2::XMLElement* stat_el = pElem->FirstChildElement(); stat_el; stat_el = stat_el->NextSiblingElement())
|
||||||
{
|
{
|
||||||
CustomLevelStat stat = {};
|
int score = 0;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
if (stat_el->GetText() != NULL)
|
if (stat_el->GetText() != NULL)
|
||||||
{
|
{
|
||||||
stat.score = help.Int(stat_el->GetText());
|
score = help.Int(stat_el->GetText());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat_el->Attribute("name"))
|
if (stat_el->Attribute("name"))
|
||||||
{
|
{
|
||||||
stat.name = stat_el->Attribute("name");
|
name = stat_el->Attribute("name");
|
||||||
}
|
}
|
||||||
|
|
||||||
customlevelstats.push_back(stat);
|
int existing = customlevelstats.count(name);
|
||||||
|
if (existing > 0)
|
||||||
|
{
|
||||||
|
file_has_duplicates = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing == 0 || score > customlevelstats[name])
|
||||||
|
{
|
||||||
|
customlevelstats[name] = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_has_duplicates)
|
||||||
|
{
|
||||||
|
/* This might be really inflated, so simply save the map we have now,
|
||||||
|
* so we don't have to keep loading a 90 MB file. */
|
||||||
|
savecustomlevelstats();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -590,8 +589,13 @@ void Game::loadcustomlevelstats(void)
|
||||||
// If the two arrays happen to differ in length, just go with the smallest one
|
// If the two arrays happen to differ in length, just go with the smallest one
|
||||||
for (size_t i = 0; i < SDL_min(customlevelnames.size(), customlevelscores.size()); i++)
|
for (size_t i = 0; i < SDL_min(customlevelnames.size(), customlevelscores.size()); i++)
|
||||||
{
|
{
|
||||||
CustomLevelStat stat = {customlevelnames[i], customlevelscores[i]};
|
const std::string& name = customlevelnames[i];
|
||||||
customlevelstats.push_back(stat);
|
const int score = customlevelscores[i];
|
||||||
|
|
||||||
|
if (customlevelstats.count(name) == 0 || score > customlevelstats[name])
|
||||||
|
{
|
||||||
|
customlevelstats[name] = score;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,29 +626,25 @@ void Game::savecustomlevelstats(void)
|
||||||
xml::update_tag(msgs, "numcustomlevelstats", numcustomlevelstats);
|
xml::update_tag(msgs, "numcustomlevelstats", numcustomlevelstats);
|
||||||
|
|
||||||
std::string customlevelscorestr;
|
std::string customlevelscorestr;
|
||||||
for(int i = 0; i < numcustomlevelstats; i++ )
|
std::string customlevelstatsstr;
|
||||||
|
std::map<std::string, int>::iterator iter;
|
||||||
|
for (iter = customlevelstats.begin(); iter != customlevelstats.end(); iter++)
|
||||||
{
|
{
|
||||||
customlevelscorestr += help.String(customlevelstats[i].score) + ",";
|
customlevelscorestr += help.String(iter->second) + ",";
|
||||||
|
customlevelstatsstr += iter->first + "|";
|
||||||
}
|
}
|
||||||
xml::update_tag(msgs, "customlevelscore", customlevelscorestr.c_str());
|
xml::update_tag(msgs, "customlevelscore", customlevelscorestr.c_str());
|
||||||
|
|
||||||
std::string customlevelstatsstr;
|
|
||||||
for(int i = 0; i < numcustomlevelstats; i++ )
|
|
||||||
{
|
|
||||||
customlevelstatsstr += customlevelstats[i].name + "|";
|
|
||||||
}
|
|
||||||
xml::update_tag(msgs, "customlevelstats", customlevelstatsstr.c_str());
|
xml::update_tag(msgs, "customlevelstats", customlevelstatsstr.c_str());
|
||||||
|
|
||||||
// New system
|
// New system
|
||||||
tinyxml2::XMLElement* msg = xml::update_element_delete_contents(msgs, "stats");
|
tinyxml2::XMLElement* msg = xml::update_element_delete_contents(msgs, "stats");
|
||||||
tinyxml2::XMLElement* stat_el;
|
tinyxml2::XMLElement* stat_el;
|
||||||
for (size_t i = 0; i < customlevelstats.size(); i++)
|
for (iter = customlevelstats.begin(); iter != customlevelstats.end(); iter++)
|
||||||
{
|
{
|
||||||
stat_el = doc.NewElement("stat");
|
stat_el = doc.NewElement("stat");
|
||||||
CustomLevelStat& stat = customlevelstats[i];
|
|
||||||
|
|
||||||
stat_el->SetAttribute("name", stat.name.c_str());
|
stat_el->SetAttribute("name", iter->first.c_str());
|
||||||
stat_el->LinkEndChild(doc.NewText(help.String(stat.score).c_str()));
|
stat_el->LinkEndChild(doc.NewText(help.String(iter->second).c_str()));
|
||||||
|
|
||||||
msg->LinkEndChild(stat_el);
|
msg->LinkEndChild(stat_el);
|
||||||
}
|
}
|
||||||
|
@ -6228,20 +6228,14 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ )
|
||||||
{
|
{
|
||||||
if(i>=levelpage*8 && i< (levelpage*8)+8)
|
if(i>=levelpage*8 && i< (levelpage*8)+8)
|
||||||
{
|
{
|
||||||
//This is, er, suboptimal. Whatever, life optimisation and all that
|
const std::string filename = cl.ListOfMetaData[i].filename.substr(7);
|
||||||
int tvar=-1;
|
int score = 0;
|
||||||
for(size_t j=0; j<customlevelstats.size(); j++)
|
if (customlevelstats.count(filename) > 0)
|
||||||
{
|
{
|
||||||
if(cl.ListOfMetaData[i].filename.substr(7) == customlevelstats[j].name)
|
score = customlevelstats[filename];
|
||||||
{
|
|
||||||
tvar=j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const char* prefix;
|
const char* prefix;
|
||||||
if(tvar>=0)
|
switch (score)
|
||||||
{
|
|
||||||
switch (customlevelstats[tvar].score)
|
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
|
@ -6266,12 +6260,6 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ )
|
||||||
prefix = "";
|
prefix = "";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static const char tmp[] = " ";
|
|
||||||
prefix = tmp;
|
|
||||||
}
|
|
||||||
char text[MENU_TEXT_BYTES];
|
char text[MENU_TEXT_BYTES];
|
||||||
SDL_snprintf(text, sizeof(text), "%s%s", prefix, cl.ListOfMetaData[i].title.c_str());
|
SDL_snprintf(text, sizeof(text), "%s%s", prefix, cl.ListOfMetaData[i].title.c_str());
|
||||||
for (size_t ii = 0; text[ii] != '\0'; ++ii)
|
for (size_t ii = 0; text[ii] != '\0'; ++ii)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define GAME_H
|
#define GAME_H
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -176,12 +177,6 @@ struct MenuStackFrame
|
||||||
enum Menu::MenuName name;
|
enum Menu::MenuName name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CustomLevelStat
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
int score; //0 - not played, 1 - finished, 2 - all trinkets, 3 - finished, all trinkets
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Game
|
class Game
|
||||||
{
|
{
|
||||||
|
@ -526,7 +521,8 @@ public:
|
||||||
void updatecustomlevelstats(std::string clevel, int cscore);
|
void updatecustomlevelstats(std::string clevel, int cscore);
|
||||||
void deletecustomlevelstats(void);
|
void deletecustomlevelstats(void);
|
||||||
|
|
||||||
std::vector<CustomLevelStat> customlevelstats;
|
// name -> score. 0 - not played, 1 - finished, 2 - all trinkets, 3 - finished, all trinkets
|
||||||
|
std::map<std::string, int> customlevelstats;
|
||||||
|
|
||||||
|
|
||||||
std::vector<SDL_GameControllerButton> controllerButton_map;
|
std::vector<SDL_GameControllerButton> controllerButton_map;
|
||||||
|
|
Loading…
Reference in a new issue