Changed: Move user landmarks from icfg to own xml file

--HG--
branch : develop
This commit is contained in:
Nimetu 2019-02-28 15:01:51 +02:00
parent 4cbed9f957
commit ac68a37f1b
8 changed files with 411 additions and 27 deletions

View file

@ -488,8 +488,177 @@ CContinent *CContinentManager::get(const std::string &contName)
return NULL;
}
void CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
void CContinentManager::writeTo(xmlNodePtr node) const
{
// <landmarks continent="bagne" type="user" />
// <landmarks continent="tryker" type="user">
// <landmark type="0" x="0" y="0" title="landmark title"/>
// ...
// </landmarks>
for(TContinents::const_iterator it = _Continents.begin(); it != _Continents.end(); ++it)
{
std::string name = it->first;
xmlNodePtr contNode = xmlNewChild(node, NULL, (const xmlChar*)"landmarks", NULL);
xmlSetProp(contNode, (const xmlChar*)"continent", (const xmlChar*)name.c_str());
xmlSetProp(contNode, (const xmlChar*)"type", (const xmlChar*)"user");
if (it->second && it->second->UserLandMarks.size() > 0)
{
for(uint i = 0; i< it->second->UserLandMarks.size(); ++i)
{
const CUserLandMark& lm = it->second->UserLandMarks[i];
xmlNodePtr lmNode = xmlNewChild(contNode, NULL, (const xmlChar*)"landmark", NULL);
xmlSetProp(lmNode, (const xmlChar*)"type", (const xmlChar*)toString("%d", (uint32)lm.Type).c_str());
xmlSetProp(lmNode, (const xmlChar*)"x", (const xmlChar*)toString("%.2f", lm.Pos.x).c_str());
xmlSetProp(lmNode, (const xmlChar*)"y", (const xmlChar*)toString("%.2f", lm.Pos.y).c_str());
// sanitize ascii control chars
// libxml will encode other special chars itself
std::string title = lm.Title.toUtf8();
for(uint i = 0; i< title.size(); i++)
{
if (title[i] < ' ' && title[i] != '\n' && title[i] != '\t')
{
title[i] = '?';
}
}
xmlSetProp(lmNode, (const xmlChar*)"title", (const xmlChar*)title.c_str());
}
}
}
}
void CContinentManager::readFrom(xmlNodePtr node)
{
CXMLAutoPtr prop;
// <landmarks continent="bagne" type="user">
// <landmark type="0" x="0" y="0" title="landmark title" />
// ...
// </landmarks>
std::string continent;
prop = xmlGetProp(node, (xmlChar*)"continent");
if (!prop)
{
nlwarning("Ignore landmarks group 'continent' attribute.");
return;
}
continent = (const char*)prop;
TContinents::iterator itContinent = _Continents.find(continent);
if (itContinent == _Continents.end() || !itContinent->second)
{
nlwarning("Ignore landmarks group with unknown 'continent' '%s'", continent.c_str());
return;
}
std::string lmtype;
prop = xmlGetProp(node, (xmlChar*)"type");
if (!prop)
{
nlwarning("Ignore landmarks group without 'type' attribute.");
return;
}
lmtype = toLower((const char*)prop);
if (lmtype != "user")
{
nlwarning("Ignore landmarks group with type '%s', expected 'user'.", lmtype.c_str());
return;
}
node = node->children;
while(node)
{
if (stricmp((char*)node->name, "landmark") != 0)
{
nlwarning("Ignore invalid node '%s' under landmarks group", (const char*)node->name);
node = node->next;
continue;
}
bool add = true;
CUserLandMark lm;
prop = xmlGetProp(node, (xmlChar*)"type");
if (prop)
fromString((const char*)prop, lm.Type);
else
nlwarning("Using default value for landmark type");
prop = xmlGetProp(node, (xmlChar*)"x");
if (prop)
{
fromString((const char*)prop, lm.Pos.x);
}
else
{
nlwarning("Landmark missing 'x' attribute");
add = false;
}
prop = xmlGetProp(node, (xmlChar*)"y");
if (prop)
{
fromString((const char*)prop, lm.Pos.y);
}
else
{
nlwarning("Landmark missing 'y' attribute");
add = false;
}
prop = xmlGetProp(node, (xmlChar*)"title");
if (prop)
{
lm.Title.fromUtf8((const char*)prop);
}
else
{
nlwarning("Landmark missing 'title' attribute");
add = false;
}
if (add)
{
// before adding, check for duplicate
// duplicates might be read from .icfg before .xml is read
add = true;
for(uint i = 0; i< itContinent->second->UserLandMarks.size(); ++i)
{
const CUserLandMark& test = itContinent->second->UserLandMarks[i];
uint xdiff = abs(test.Pos.x - lm.Pos.x) * 100;
uint ydiff = abs(test.Pos.y - lm.Pos.y) * 100;
if (xdiff == 0 && ydiff == 0)
{
add = false;
break;
}
}
if (add)
{
itContinent->second->UserLandMarks.push_back(lm);
}
else
{
nlwarning("Ignore landmark with duplicate pos (continent:'%s', x:%.2f, y:%.2f, type:%d, title:'%s')", continent.c_str(), lm.Pos.x, lm.Pos.y, (uint8)lm.Type, lm.Title.toUtf8().c_str());
}
}
else
{
nlwarning("Landmark not added");
}
node = node->next;
}
}
uint32 CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
{
uint32 totalLandmarks = 0;
f.serialVersion(1);
if (!f.isReading())
{
@ -502,6 +671,7 @@ void CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
if (it->second)
{
f.serialCont(it->second->UserLandMarks);
totalLandmarks += it->second->UserLandMarks.size();
}
else
{
@ -522,6 +692,7 @@ void CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
if (it != _Continents.end() && it->second)
{
f.serialCont(it->second->UserLandMarks);
totalLandmarks += it->second->UserLandMarks.size();
}
else
{
@ -530,6 +701,8 @@ void CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
}
}
}
return totalLandmarks;
}

View file

@ -109,8 +109,13 @@ public:
const std::string &getCurrentContinentSelectName();
// load / save all user landmarks in xml format
void writeTo(xmlNodePtr node) const;
void readFrom(xmlNodePtr node);
// load / saves all user landMarks
void serialUserLandMarks(NLMISC::IStream &f);
// \return number of landmarks loaded or saved
uint32 serialUserLandMarks(NLMISC::IStream &f);
// rebuild visible landmarks on current map
void updateUserLandMarks();

View file

@ -1298,6 +1298,7 @@ void CFarTP::sendReady()
pIM->loadKeys();
CWidgetManager::getInstance()->hideAllWindows();
pIM->loadInterfaceConfig();
pIM->loadLandmarks();
}
else
{

View file

@ -2715,7 +2715,7 @@ CCtrlButton *CGroupMap::addUserLandMark(const NLMISC::CVector2f &pos, const ucst
addLandMark(_UserLM, pos, title, getUserLandMarkOptions((uint32)_CurContinent->UserLandMarks.size() - 1));
// Save the config file each time a user landmark is created
CInterfaceManager::getInstance()->saveConfig();
CInterfaceManager::getInstance()->saveLandmarks();
return _UserLM.back();
}

View file

@ -458,6 +458,7 @@ CInterfaceManager::CInterfaceManager()
parser->addModule( "command", new CCommandParser() );
parser->addModule( "key", new CKeyParser() );
parser->addModule( "macro", new CMacroParser() );
parser->addModule( "landmarks", new CLandmarkParser() );
parser->setCacheUIParsing( ClientCfg.CacheUIParsing );
CViewRenderer::setDriver( Driver );
@ -982,6 +983,9 @@ void CInterfaceManager::initInGame()
// Interface config
loadInterfaceConfig();
//Load user landmarks
loadLandmarks();
// Must do extra init for people interaction after load
PeopleInterraction.initAfterLoad();
@ -1217,6 +1221,22 @@ void CInterfaceManager::configureQuitDialogBox()
}
}
// ------------------------------------------------------------------------------------------------
//
std::string CInterfaceManager::getSaveFileName(const std::string &module, const std::string &ext, bool useShared) const
{
string filename = "save/" + module + "_" + PlayerSelectedFileName + "." + ext;
if (useShared && !CFile::fileExists(filename))
{
string sharedFile = "save/shared_" + module + "." + ext;
if (CFile::fileExists(sharedFile))
{
return sharedFile;
}
}
return filename;
}
// ------------------------------------------------------------------------------------------------
void CInterfaceManager::loadKeys()
{
@ -1228,26 +1248,19 @@ void CInterfaceManager::loadKeys()
vector<string> xmlFilesToParse;
// Does the keys file exist ?
string userKeyFileName = "save/keys_"+PlayerSelectedFileName+".xml";
string userKeyFileName = getSaveFileName("keys", "xml");
if (CFile::fileExists(userKeyFileName) && CFile::getFileSize(userKeyFileName) > 0)
{
// Load the user key file
xmlFilesToParse.push_back (userKeyFileName);
}
else
{
string filename = "save/shared_keys.xml";
if(CFile::fileExists(filename) && CFile::getFileSize(filename) > 0)
{
xmlFilesToParse.push_back(filename);
}
}
// Load the default key (but don't replace existings bounds, see keys.xml "key_def_no_replace")
xmlFilesToParse.push_back ("keys.xml");
if (!parseInterface (xmlFilesToParse, true))
{
badXMLParseMessageBox();
createFileBackup("Error loading keys", userKeyFileName);
}
_KeysLoaded = true;
@ -1260,10 +1273,7 @@ void CInterfaceManager::loadInterfaceConfig()
if (ClientCfg.R2EDEnabled) // in R2ED mode the CEditor class deals with it
return;
string filename = "save/interface_" + PlayerSelectedFileName + ".icfg";
if (!CFile::fileExists(filename))
filename = "save/shared_interface.icfg";
string filename = getSaveFileName("interface", "icfg");
loadConfig(filename); // Invalidate coords of changed groups
_ConfigLoaded = true;
@ -1680,6 +1690,7 @@ bool CInterfaceManager::loadConfig (const string &filename)
uint32 nNbMode;
CInterfaceConfig ic;
bool lastInGameScreenResLoaded= false;
uint32 nbLandmarks = 0;
try
{
sint ver = f.serialVersion(ICFG_STREAM_VERSION);
@ -1775,7 +1786,7 @@ bool CInterfaceManager::loadConfig (const string &filename)
}
// Load user landmarks
ContinentMngr.serialUserLandMarks(f);
nbLandmarks = ContinentMngr.serialUserLandMarks(f);
CCDBNodeLeaf *pNL = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:INTERFACES:NB_BONUS_LANDMARKS" );
if ( pNL )
@ -1809,10 +1820,9 @@ bool CInterfaceManager::loadConfig (const string &filename)
catch(const NLMISC::EStream &)
{
f.close();
string sFileNameBackup = sFileName+"backup";
if (CFile::fileExists(sFileNameBackup))
CFile::deleteFile(sFileNameBackup);
CFile::moveFile(sFileNameBackup, sFileName);
createFileBackup("Config loading failed", sFileName);
nlwarning("Config loading failed : restore default");
vector<string> v;
if (!ClientCfg.R2EDEnabled)
@ -1823,6 +1833,35 @@ bool CInterfaceManager::loadConfig (const string &filename)
}
f.close();
if (nbLandmarks > 0)
{
// use copy for backup so on save proper shared/player icfg file is used
createFileBackup("Landmarks will be migrated to xml", sFileName, true);
// if icfg is interface_player.icfg, then landmarks must also be loaded/saved to player file
if (nlstricmp(sFileName.substr(0, 12), "save/shared_") != 0)
{
string lmfile = getSaveFileName("landmarks", "xml", false);
if (!CFile::fileExists(lmfile))
{
// create placeholder player landmarks file so saveLandmarks will use it
// even if shared landmarks file exists
COFile f;
if (f.open(lmfile, false, false, true))
{
std::string xml;
xml = "<?xml version=\"1.0\"?>\n<interface_config />";
f.serialBuffer((uint8 *)xml.c_str(), xml.size());
f.close();
}
}
}
// merge .icfg landmarks with landmarks.xml
loadLandmarks();
saveLandmarks();
}
// *** If saved resolution is different from the current one setuped, must fix positions in _Modes
if(lastInGameScreenResLoaded)
{
@ -1877,6 +1916,88 @@ public:
}
};
bool CInterfaceManager::saveLandmarks(bool verbose) const
{
bool ret = true;
if (!ClientCfg.R2EDEnabled)
{
uint8 currMode = getMode();
string filename = getSaveFileName("landmarks", "xml");
if (verbose) CInterfaceManager::getInstance()->displaySystemInfo("Saving " + filename);
ret = saveLandmarks(filename);
}
return ret;
}
bool CInterfaceManager::loadLandmarks()
{
// Does the keys file exist ?
string filename = getSaveFileName("landmarks", "xml");
CIFile f;
string sFileName;
sFileName = NLMISC::CPath::lookup (filename, false);
if (sFileName.empty() || !f.open(sFileName))
return false;
bool ret = false;
vector<string> xmlFilesToParse;
xmlFilesToParse.push_back (filename);
//ContinentMngr.serialUserLandMarks(node);
if (!parseInterface (xmlFilesToParse, true))
{
f.close();
createFileBackup("Error while loading landmarks", filename);
ret = false;
}
return ret;
}
bool CInterfaceManager::saveLandmarks(const std::string &filename) const
{
nlinfo( "Saving landmarks : %s", filename.c_str() );
bool ret = false;
try
{
COFile f;
// using temporary file, so no f.close() unless its a success
if (f.open(filename, false, false, true))
{
COXml xmlStream;
xmlStream.init (&f);
xmlDocPtr doc = xmlStream.getDocument ();
xmlNodePtr node = xmlNewDocNode(doc, NULL, (const xmlChar*)"interface_config", NULL);
xmlDocSetRootElement (doc, node);
ContinentMngr.writeTo(node);
// Flush the stream
xmlStream.flush();
// Close the stream
f.close ();
ret = true;
}
}
catch (const Exception &e)
{
nlwarning ("Error while writing the file %s : %s.", filename.c_str(), e.what ());
}
return ret;
}
// ------------------------------------------------------------------------------------------------
//
bool CInterfaceManager::saveConfig (bool verbose)
@ -1887,9 +2008,7 @@ bool CInterfaceManager::saveConfig (bool verbose)
{
uint8 currMode = getMode();
string filename = "save/interface_" + PlayerSelectedFileName + ".icfg";
if (!CFile::fileExists(filename) && CFile::fileExists("save/shared_interface.icfg"))
filename = "save/shared_interface.icfg";
string filename = getSaveFileName("interface", "icfg");
if (verbose) CInterfaceManager::getInstance()->displaySystemInfo("Saving " + filename);
ret = saveConfig(filename);
@ -2004,8 +2123,13 @@ bool CInterfaceManager::saveConfig (const string &filename)
CTaskBarManager *pTBM= CTaskBarManager::getInstance();
pTBM->serial(f);
// Save user landmarks
ContinentMngr.serialUserLandMarks(f);
//ContinentMngr.serialUserLandMarks(f);
// empty landmarks block for compatibility
{
f.serialVersion(1);
uint32 numCont = 0;
f.serial(numCont);
}
// Info Windows position.
CInterfaceHelp::serialInfoWindows(f);
@ -2972,6 +3096,7 @@ class CAHSaveUI : public IActionHandler
{
CInterfaceManager::getInstance()->saveKeys(true);
CInterfaceManager::getInstance()->saveConfig(true);
CInterfaceManager::getInstance()->saveLandmarks(true);
}
};
REGISTER_ACTION_HANDLER (CAHSaveUI, "save_ui");
@ -4135,3 +4260,40 @@ bool CInterfaceManager::parseTokens(ucstring& ucstr)
ucstr = str;
return true;;
}
std::string CInterfaceManager::getNextBackupName(std::string filename)
{
std::string ts = getTimestampHuman("%Y-%m-%d");
if (!ts.empty())
{
std::string::size_type pos = filename.find_last_of('.');
if (pos == std::string::npos)
filename = filename + "_" + ts + "_";
else
filename = filename.substr(0, pos) + "_" + ts + "_" + filename.substr(pos);
}
// filename_YYYY-MM-DD_000.ext
return CFile::findNewFile(filename);
}
void CInterfaceManager::createFileBackup(const std::string &message, const std::string &filename, bool useCopy)
{
std::string backupName = getNextBackupName(filename);
nlwarning("%s: '%s'.", message.c_str(), filename.c_str());
if (!backupName.empty())
{
if (useCopy)
{
nlwarning("Backup copy saved as '%s'", backupName.c_str());
CFile::copyFile(backupName, filename);
}
else
{
nlwarning("File renamed to '%s'", backupName.c_str());
CFile::moveFile(backupName, filename);
}
}
}

View file

@ -203,6 +203,19 @@ public:
/// Load a set of xml files
bool parseInterface (const std::vector<std::string> &xmlFileNames, bool reload, bool isFilename = true);
/// return new filename that can be used to backup original file
std::string getNextBackupName(std::string filename);
/// copy/rename filename for backup and show error in log
void createFileBackup(const std::string &message, const std::string &filename, bool useCopy = false);
/// select player/shared file name from 'save' folder'
std::string getSaveFileName(const std::string &module, const std::string &ext, bool useShared = true) const;
/// Load / save user landmarks in .xml format
bool loadLandmarks ();
bool saveLandmarks (bool verbose = false) const;
bool saveLandmarks (const std::string &filename) const;
// Load/Save position, size, etc.. of windows
bool loadConfig (const std::string &filename);
// Save config to default location, if verbose is true, display message in game sysinfo

View file

@ -24,6 +24,7 @@
#include "../commands.h"
#include "interface_3d_scene.h"
#include "nel/misc/i_xml.h"
#include "../continent_manager.h"
using namespace NLMISC;
@ -31,6 +32,8 @@ using namespace NLMISC;
#include "../client_cfg.h"
#endif
extern CContinentManager ContinentMngr;
CIF3DSceneParser::CIF3DSceneParser()
{
parsingStage |= ( Resolved | GroupChildren );
@ -529,3 +532,21 @@ bool CMacroParser::parse( xmlNodePtr cur, NLGUI::CInterfaceGroup *parentGroup )
return true;
}
CLandmarkParser::CLandmarkParser()
{
parsingStage |= Unresolved;
}
CLandmarkParser::~CLandmarkParser()
{
}
bool CLandmarkParser::parse( xmlNodePtr cur, NLGUI::CInterfaceGroup *parentGroup )
{
H_AUTO(parseLandmark)
ContinentMngr.readFrom(cur);
return true;
}

View file

@ -76,4 +76,13 @@ public:
bool parse( xmlNodePtr cur, CInterfaceGroup *parentGroup );
};
class CLandmarkParser : public CInterfaceParser::IParserModule
{
public:
CLandmarkParser();
~CLandmarkParser();
bool parse( xmlNodePtr cur, CInterfaceGroup *parentGroup );
};
#endif