libkdegames Library API Documentation

kexthighscore_internal.cpp

00001 /*
00002     This file is part of the KDE games library
00003     Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org)
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License version 2 as published by the Free Software Foundation.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017     Boston, MA 02111-1307, USA.
00018 */
00019 
00020 #include <config.h>
00021 
00022 #include "kexthighscore_internal.h"
00023 
00024 #include <pwd.h>
00025 #include <sys/types.h>
00026 #include <unistd.h>
00027 
00028 #include <qfile.h>
00029 #include <qlayout.h>
00030 #include <qdom.h>
00031 
00032 #include <kglobal.h>
00033 #include <kio/netaccess.h>
00034 #include <kio/job.h>
00035 #include <kmessagebox.h>
00036 #include <kmdcodec.h>
00037 #include <kdebug.h>
00038 
00039 #include "kexthighscore.h"
00040 #include "kexthighscore_gui.h"
00041 #include "kemailsettings.h"
00042 
00043 
00044 namespace KExtHighscore
00045 {
00046 
00047 //-----------------------------------------------------------------------------
00048 const char ItemContainer::ANONYMOUS[] = "_";
00049 const char ItemContainer::ANONYMOUS_LABEL[] = I18N_NOOP("anonymous");
00050 
00051 ItemContainer::ItemContainer()
00052     : _item(0)
00053 {}
00054 
00055 ItemContainer::~ItemContainer()
00056 {
00057     delete _item;
00058 }
00059 
00060 void ItemContainer::setItem(Item *item)
00061 {
00062     delete _item;
00063     _item = item;
00064 }
00065 
00066 QString ItemContainer::entryName() const
00067 {
00068     if ( _subGroup.isEmpty() ) return _name;
00069     return _name + "_" + _subGroup;
00070 }
00071 
00072 QVariant ItemContainer::read(uint i) const
00073 {
00074     Q_ASSERT(_item);
00075 
00076     QVariant v = _item->defaultValue();
00077     if ( isStored() ) {
00078         internal->hsConfig().setHighscoreGroup(_group);
00079         v = internal->hsConfig().readPropertyEntry(i+1, entryName(), v);
00080     }
00081     return _item->read(i, v);
00082 }
00083 
00084 QString ItemContainer::pretty(uint i) const
00085 {
00086     Q_ASSERT(_item);
00087     return _item->pretty(i, read(i));
00088 }
00089 
00090 void ItemContainer::write(uint i, const QVariant &value) const
00091 {
00092     Q_ASSERT( isStored() );
00093     Q_ASSERT( internal->hsConfig().isLocked() );
00094     internal->hsConfig().setHighscoreGroup(_group);
00095     internal->hsConfig().writeEntry(i+1, entryName(), value);
00096 }
00097 
00098 uint ItemContainer::increment(uint i) const
00099 {
00100     uint v = read(i).toUInt() + 1;
00101     write(i, v);
00102     return v;
00103 }
00104 
00105 //-----------------------------------------------------------------------------
00106 ItemArray::ItemArray()
00107     : _group(""), _subGroup("") // no null groups
00108 {}
00109 
00110 ItemArray::~ItemArray()
00111 {
00112     for (uint i=0; i<size(); i++) delete at(i);
00113 }
00114 
00115 int ItemArray::findIndex(const QString &name) const
00116 {
00117     for (uint i=0; i<size(); i++)
00118         if ( at(i)->name()==name ) return i;
00119     return -1;
00120 }
00121 
00122 const ItemContainer *ItemArray::item(const QString &name) const
00123 {
00124     int i = findIndex(name);
00125     if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
00126                                 << "\"" << endl;
00127     return at(i);
00128 }
00129 
00130 ItemContainer *ItemArray::item(const QString &name)
00131 {
00132     int i = findIndex(name);
00133     if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
00134                                 << "\"" << endl;
00135     return at(i);
00136 }
00137 
00138 void ItemArray::setItem(const QString &name, Item *item)
00139 {
00140     int i = findIndex(name);
00141     if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
00142                                 << "\"" << endl;
00143     bool stored = at(i)->isStored();
00144     bool canHaveSubGroup = at(i)->canHaveSubGroup();
00145     _setItem(i, name, item, stored, canHaveSubGroup);
00146 }
00147 
00148 void ItemArray::addItem(const QString &name, Item *item,
00149                         bool stored, bool canHaveSubGroup)
00150 {
00151     if ( findIndex(name)!=-1 )
00152         kdError(11002) << "item already exists \"" << name << "\"" << endl;
00153     uint i = size();
00154     resize(i+1);
00155     at(i) = new ItemContainer;
00156     _setItem(i, name, item, stored, canHaveSubGroup);
00157 }
00158 
00159 void ItemArray::_setItem(uint i, const QString &name, Item *item,
00160                          bool stored, bool canHaveSubGroup)
00161 {
00162     at(i)->setItem(item);
00163     at(i)->setName(name);
00164     at(i)->setGroup(stored ? _group : QString::null);
00165     at(i)->setSubGroup(canHaveSubGroup ? _subGroup : QString::null);
00166 }
00167 
00168 void ItemArray::setGroup(const QString &group)
00169 {
00170     Q_ASSERT( !group.isNull() );
00171     _group = group;
00172     for (uint i=0; i<size(); i++)
00173         if ( at(i)->isStored() ) at(i)->setGroup(group);
00174 }
00175 
00176 void ItemArray::setSubGroup(const QString &subGroup)
00177 {
00178     Q_ASSERT( !subGroup.isNull() );
00179     _subGroup = subGroup;
00180     for (uint i=0; i<size(); i++)
00181         if ( at(i)->canHaveSubGroup() ) at(i)->setSubGroup(subGroup);
00182 }
00183 
00184 void ItemArray::read(uint k, Score &data) const
00185 {
00186     for (uint i=0; i<size(); i++) {
00187         if ( !at(i)->isStored() ) continue;
00188         data.setData(at(i)->name(), at(i)->read(k));
00189     }
00190 }
00191 
00192 void ItemArray::write(uint k, const Score &data, uint nb) const
00193 {
00194     for (uint i=0; i<size(); i++) {
00195         if ( !at(i)->isStored() ) continue;
00196         for (uint j=nb-1; j>k; j--)  at(i)->write(j, at(i)->read(j-1));
00197         at(i)->write(k, data.data(at(i)->name()));
00198     }
00199 }
00200 
00201 void ItemArray::exportToText(QTextStream &s) const
00202 {
00203     for (uint k=0; k<nbEntries()+1; k++) {
00204         for (uint i=0; i<size(); i++) {
00205             const Item *item = at(i)->item();
00206             if ( item->isVisible() ) {
00207                 if ( i!=0 ) s << '\t';
00208                 if ( k==0 ) s << item->label();
00209                 else s << at(i)->pretty(k-1);
00210             }
00211         }
00212         s << endl;
00213     }
00214 }
00215 
00216 //-----------------------------------------------------------------------------
00217 class ScoreNameItem : public NameItem
00218 {
00219  public:
00220     ScoreNameItem(const ScoreInfos &score, const PlayerInfos &infos)
00221         : _score(score), _infos(infos) {}
00222 
00223     QString pretty(uint i, const QVariant &v) const {
00224         uint id = _score.item("id")->read(i).toUInt();
00225         if ( id==0 ) return NameItem::pretty(i, v);
00226         return _infos.prettyName(id-1);
00227     }
00228 
00229  private:
00230     const ScoreInfos  &_score;
00231     const PlayerInfos &_infos;
00232 };
00233 
00234 //-----------------------------------------------------------------------------
00235 ScoreInfos::ScoreInfos(uint maxNbEntries, const PlayerInfos &infos)
00236     : _maxNbEntries(maxNbEntries)
00237 {
00238     addItem("id", new Item((uint)0));
00239     addItem("rank", new RankItem, false);
00240     addItem("name", new ScoreNameItem(*this, infos));
00241     addItem("score", Manager::createItem(Manager::ScoreDefault));
00242     addItem("date", new DateItem);
00243 }
00244 
00245 uint ScoreInfos::nbEntries() const
00246 {
00247     uint i = 0;
00248     for (; i<_maxNbEntries; i++)
00249         if ( item("score")->read(i)==item("score")->item()->defaultValue() )
00250             break;
00251     return i;
00252 }
00253 
00254 //-----------------------------------------------------------------------------
00255 const char *HS_ID              = "player id";
00256 const char *HS_REGISTERED_NAME = "registered name";
00257 const char *HS_KEY             = "player key";
00258 const char *HS_WW_ENABLED      = "ww hs enabled";
00259 
00260 PlayerInfos::PlayerInfos()
00261 {
00262     setGroup("players");
00263 
00264     // standard items
00265     addItem("name", new NameItem);
00266     Item *it = new Item((uint)0, i18n("Games Count"),Qt::AlignRight);
00267     addItem("nb games", it, true, true);
00268     it = Manager::createItem(Manager::MeanScoreDefault);
00269     addItem("mean score", it, true, true);
00270     it = Manager::createItem(Manager::BestScoreDefault);
00271     addItem("best score", it, true, true);
00272     addItem("date", new DateItem, true, true);
00273     it = new Item(QString::null, i18n("Comment"), Qt::AlignLeft);
00274     addItem("comment", it);
00275 
00276     // statistics items
00277     addItem("nb black marks", new Item((uint)0), true, true); // legacy
00278     addItem("nb lost games", new Item((uint)0), true, true);
00279     addItem("nb draw games", new Item((uint)0), true, true);
00280     addItem("current trend", new Item((int)0), true, true);
00281     addItem("max lost trend", new Item((uint)0), true, true);
00282     addItem("max won trend", new Item((uint)0), true, true);
00283 
00284     struct passwd *pwd = getpwuid(getuid());
00285     QString username = pwd->pw_name;
00286 #ifdef HIGHSCORE_DIRECTORY
00287     internal->hsConfig().setHighscoreGroup("players");
00288     for (uint i=0; ;i++) {
00289         if ( !internal->hsConfig().hasEntry(i+1, "username") ) {
00290             _newPlayer = true;
00291             _id = i;
00292             break;
00293         }
00294         if ( internal->hsConfig().readEntry(i+1, "username")==username ) {
00295             _newPlayer = false;
00296             _id = i;
00297             return;
00298         }
00299     }
00300 #endif
00301     internal->hsConfig().lockForWriting();
00302     KEMailSettings emailConfig;
00303     emailConfig.setProfile(emailConfig.defaultProfileName());
00304     QString name = emailConfig.getSetting(KEMailSettings::RealName);
00305     if ( name.isEmpty() || isNameUsed(name) ) name = username;
00306     if ( isNameUsed(name) ) name= QString(ItemContainer::ANONYMOUS);
00307 #ifdef HIGHSCORE_DIRECTORY
00308     internal->hsConfig().writeEntry(_id+1, "username", username);
00309     item("name")->write(_id, name);
00310 #endif
00311 
00312     ConfigGroup cg;
00313     _oldLocalPlayer = cg.config()->hasKey(HS_ID);
00314     _oldLocalId = cg.config()->readUnsignedNumEntry(HS_ID);
00315 #ifdef HIGHSCORE_DIRECTORY
00316     if (_oldLocalPlayer) { // player already exists in local config file
00317         // copy player data
00318         QString prefix = QString("%1_").arg(_oldLocalId+1);
00319         QMap<QString, QString> entries =
00320             cg.config()->entryMap("KHighscore_players");
00321         QMap<QString, QString>::const_iterator it;
00322         for (it=entries.begin(); it!=entries.end(); ++it) {
00323             QString key = it.key();
00324             if ( key.find(prefix)==0 ) {
00325                 QString name = key.right(key.length()-prefix.length());
00326                 if ( name!="name" || !isNameUsed(it.data()) )
00327                     internal->hsConfig().writeEntry(_id+1, name, it.data());
00328             }
00329         }
00330     }
00331 #else
00332     _newPlayer = !_oldLocalPlayer;
00333     if (_oldLocalPlayer) _id = _oldLocalId;
00334     else {
00335         _id = nbEntries();
00336         cg.config()->writeEntry(HS_ID, _id);
00337         item("name")->write(_id, name);
00338     }
00339 #endif
00340     internal->hsConfig().writeAndUnlock();
00341 }
00342 
00343 void PlayerInfos::createHistoItems(const QMemArray<uint> &scores, bool bound)
00344 {
00345     Q_ASSERT( _histogram.size()==0 );
00346     _bound = bound;
00347     _histogram = scores;
00348     for (uint i=1; i<histoSize(); i++)
00349         addItem(histoName(i), new Item((uint)0), true, true);
00350 }
00351 
00352 bool PlayerInfos::isAnonymous() const
00353 {
00354     return ( name()==ItemContainer::ANONYMOUS );
00355 }
00356 
00357 uint PlayerInfos::nbEntries() const
00358 {
00359     internal->hsConfig().setHighscoreGroup("players");
00360     QStringList list = internal->hsConfig().readList("name", -1);
00361     return list.count();
00362 }
00363 
00364 QString PlayerInfos::key() const
00365 {
00366     ConfigGroup cg;
00367     return cg.config()->readEntry(HS_KEY, QString::null);
00368 }
00369 
00370 bool PlayerInfos::isWWEnabled() const
00371 {
00372     ConfigGroup cg;
00373     return cg.config()->readBoolEntry(HS_WW_ENABLED, false);
00374 }
00375 
00376 QString PlayerInfos::histoName(uint i) const
00377 {
00378     const QMemArray<uint> &sh = _histogram;
00379     Q_ASSERT( i<sh.size() || (_bound || i==sh.size()) );
00380     if ( i==sh.size() )
00381         return QString("nb scores greater than %1").arg(sh[sh.size()-1]);
00382     return QString("nb scores less than %1").arg(sh[i]);
00383 }
00384 
00385 uint PlayerInfos::histoSize() const
00386 {
00387      return _histogram.size() + (_bound ? 0 : 1);
00388 }
00389 
00390 void PlayerInfos::submitScore(const Score &score) const
00391 {
00392     // update counts
00393     uint nbGames = item("nb games")->increment(_id);
00394     switch (score.type()) {
00395     case Lost:
00396         item("nb lost games")->increment(_id);
00397         break;
00398     case Won: break;
00399     case Draw:
00400         item("nb draw games")->increment(_id);
00401         break;
00402     };
00403 
00404     // update mean
00405     if ( score.type()==Won ) {
00406         uint nbWonGames = nbGames - item("nb lost games")->read(_id).toUInt()
00407                         - item("nb draw games")->read(_id).toUInt()
00408                         - item("nb black marks")->read(_id).toUInt(); // legacy
00409         double mean = (nbWonGames==1 ? 0.0
00410                        : item("mean score")->read(_id).toDouble());
00411         mean += (double(score.score()) - mean) / nbWonGames;
00412         item("mean score")->write(_id, mean);
00413     }
00414 
00415     // update best score
00416     Score best = score; // copy optionnal fields (there are not taken into account here)
00417     best.setScore( item("best score")->read(_id).toUInt() );
00418     if ( best<score ) {
00419         item("best score")->write(_id, score.score());
00420         item("date")->write(_id, score.data("date").toDateTime());
00421     }
00422 
00423     // update trends
00424     int current = item("current trend")->read(_id).toInt();
00425     switch (score.type()) {
00426     case Won: {
00427         if ( current<0 ) current = 0;
00428         current++;
00429         uint won = item("max won trend")->read(_id).toUInt();
00430         if ( (uint)current>won ) item("max won trend")->write(_id, current);
00431         break;
00432     }
00433     case Lost: {
00434         if ( current>0 ) current = 0;
00435         current--;
00436         uint lost = item("max lost trend")->read(_id).toUInt();
00437         uint clost = -current;
00438         if ( clost>lost ) item("max lost trend")->write(_id, clost);
00439         break;
00440     }
00441     case Draw:
00442         current = 0;
00443         break;
00444     }
00445     item("current trend")->write(_id, current);
00446 
00447     // update histogram
00448     if ( score.type()==Won ) {
00449         const QMemArray<uint> &sh = _histogram;
00450         for (uint i=1; i<histoSize(); i++)
00451             if ( i==sh.size() || score.score()<sh[i] ) {
00452                 item(histoName(i))->increment(_id);
00453                 break;
00454             }
00455     }
00456 }
00457 
00458 bool PlayerInfos::isNameUsed(const QString &newName) const
00459 {
00460     if ( newName==name() ) return false; // own name...
00461     for (uint i=0; i<nbEntries(); i++)
00462         if ( newName.lower()==item("name")->read(i).toString().lower() ) return true;
00463     if ( newName==i18n(ItemContainer::ANONYMOUS_LABEL) ) return true;
00464     return false;
00465 }
00466 
00467 void PlayerInfos::modifyName(const QString &newName) const
00468 {
00469     item("name")->write(_id, newName);
00470 }
00471 
00472 void PlayerInfos::modifySettings(const QString &newName,
00473                                  const QString &comment, bool WWEnabled,
00474                                  const QString &newKey) const
00475 {
00476     modifyName(newName);
00477     item("comment")->write(_id, comment);
00478     ConfigGroup cg;
00479     cg.config()->writeEntry(HS_WW_ENABLED, WWEnabled);
00480     if ( !newKey.isEmpty() ) cg.config()->writeEntry(HS_KEY, newKey);
00481     if (WWEnabled) cg.config()->writeEntry(HS_REGISTERED_NAME, newName);
00482 }
00483 
00484 QString PlayerInfos::registeredName() const
00485 {
00486     ConfigGroup cg;
00487     return cg.config()->readEntry(HS_REGISTERED_NAME, QString::null);
00488 }
00489 
00490 void PlayerInfos::removeKey()
00491 {
00492     ConfigGroup cg;
00493 
00494     // save old key/nickname
00495     uint i = 0;
00496     QString str = "%1 old #%2";
00497     QString sk;
00498     do {
00499         i++;
00500         sk = str.arg(HS_KEY).arg(i);
00501     } while ( !cg.config()->readEntry(sk, QString::null).isEmpty() );
00502     cg.config()->writeEntry(sk, key());
00503     cg.config()->writeEntry(str.arg(HS_REGISTERED_NAME).arg(i),
00504                             registeredName());
00505 
00506     // clear current key/nickname
00507     cg.config()->deleteEntry(HS_KEY);
00508     cg.config()->deleteEntry(HS_REGISTERED_NAME);
00509     cg.config()->writeEntry(HS_WW_ENABLED, false);
00510 }
00511 
00512 //-----------------------------------------------------------------------------
00513 ManagerPrivate::ManagerPrivate(uint nbGameTypes, Manager &m)
00514     : manager(m), showStatistics(false), showDrawGames(false),
00515       trackLostGames(false), trackDrawGames(false),
00516       showMode(Manager::ShowForHigherScore),
00517       _first(true), _nbGameTypes(nbGameTypes), _gameType(0)
00518 {}
00519 
00520 void ManagerPrivate::init(uint maxNbEntries)
00521 {
00522     _hsConfig = new KHighscore(false, 0);
00523     _playerInfos = new PlayerInfos;
00524     _scoreInfos = new ScoreInfos(maxNbEntries, *_playerInfos);
00525 }
00526 
00527 ManagerPrivate::~ManagerPrivate()
00528 {
00529     delete _scoreInfos;
00530     delete _playerInfos;
00531     delete _hsConfig;
00532 }
00533 
00534 KURL ManagerPrivate::queryURL(QueryType type, const QString &newName) const
00535 {
00536     KURL url = serverURL;
00537     QString nameItem = "nickname";
00538     QString name = _playerInfos->registeredName();
00539     bool withVersion = true;
00540     bool key = false;
00541     bool level = false;
00542 
00543     switch (type) {
00544         case Submit:
00545             url.addPath("submit.php");
00546             level = true;
00547             key = true;
00548             break;
00549         case Register:
00550             url.addPath("register.php");
00551             name = newName;
00552             break;
00553         case Change:
00554             url.addPath("change.php");
00555             key = true;
00556             if ( newName!=name )
00557                 Manager::addToQueryURL(url, "new_nickname", newName);
00558             break;
00559         case Players:
00560             url.addPath("players.php");
00561             nameItem = "highlight";
00562             withVersion = false;
00563             break;
00564         case Scores:
00565             url.addPath("highscores.php");
00566             withVersion = false;
00567             if ( _nbGameTypes>1 ) level = true;
00568             break;
00569     }
00570 
00571     if (withVersion) Manager::addToQueryURL(url, "version", version);
00572     if ( !name.isEmpty() ) Manager::addToQueryURL(url, nameItem, name);
00573     if (key) Manager::addToQueryURL(url, "key", _playerInfos->key());
00574     if (level) {
00575         QString label = manager.gameTypeLabel(_gameType, Manager::WW);
00576         if ( !label.isEmpty() ) Manager::addToQueryURL(url, "level", label);
00577     }
00578 
00579     return url;
00580 }
00581 
00582 // strings that needs to be translated (coming from the highscores server)
00583 const char *DUMMY_STRINGS[] = {
00584     I18N_NOOP("Undefined error."),
00585     I18N_NOOP("Missing argument(s)."),
00586     I18N_NOOP("Invalid argument(s)."),
00587 
00588     I18N_NOOP("Unable to connect to MySQL server."),
00589     I18N_NOOP("Unable to select database."),
00590     I18N_NOOP("Error on database query."),
00591     I18N_NOOP("Error on database insert."),
00592 
00593     I18N_NOOP("Nickname already registered."),
00594     I18N_NOOP("Nickname not registered."),
00595     I18N_NOOP("Invalid key."),
00596     I18N_NOOP("Invalid submit key."),
00597 
00598     I18N_NOOP("Invalid level."),
00599     I18N_NOOP("Invalid score.")
00600 };
00601 
00602 const char *UNABLE_TO_CONTACT =
00603     I18N_NOOP("Unable to contact world-wide highscore server");
00604 
00605 bool ManagerPrivate::doQuery(const KURL &url, QWidget *parent,
00606                                 QDomNamedNodeMap *map)
00607 {
00608     KIO::http_update_cache(url, true, 0); // remove cache !
00609 
00610     QString tmpFile;
00611     if ( !KIO::NetAccess::download(url, tmpFile, parent) ) {
00612         QString details = i18n("Server URL: %1").arg(url.host());
00613         KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details);
00614         return false;
00615     }
00616 
00617     QFile file(tmpFile);
00618     if ( !file.open(IO_ReadOnly) ) {
00619         KIO::NetAccess::removeTempFile(tmpFile);
00620         QString details = i18n("Unable to open temporary file.");
00621         KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details);
00622         return false;
00623     }
00624 
00625     QTextStream t(&file);
00626     QString content = t.read().stripWhiteSpace();
00627     file.close();
00628     KIO::NetAccess::removeTempFile(tmpFile);
00629 
00630     QDomDocument doc;
00631     if ( doc.setContent(content) ) {
00632         QDomElement root = doc.documentElement();
00633         QDomElement element = root.firstChild().toElement();
00634         if ( element.tagName()=="success" ) {
00635             if (map) *map = element.attributes();
00636             return true;
00637         }
00638         if ( element.tagName()=="error" ) {
00639             QDomAttr attr = element.attributes().namedItem("label").toAttr();
00640             if ( !attr.isNull() ) {
00641                 QString msg = i18n(attr.value().latin1());
00642                 QString caption = i18n("Message from world-wide highscores "
00643                                        "server");
00644                 KMessageBox::sorry(parent, msg, caption);
00645                 return false;
00646             }
00647         }
00648     }
00649     QString msg = i18n("Invalid answer from world-wide highscores server.");
00650     QString details = i18n("Raw message: %1").arg(content);
00651     KMessageBox::detailedSorry(parent, msg, details);
00652     return false;
00653 }
00654 
00655 bool ManagerPrivate::getFromQuery(const QDomNamedNodeMap &map,
00656                                   const QString &name, QString &value,
00657                                   QWidget *parent)
00658 {
00659     QDomAttr attr = map.namedItem(name).toAttr();
00660     if ( attr.isNull() ) {
00661         KMessageBox::sorry(parent,
00662                i18n("Invalid answer from world-wide "
00663                     "highscores server (missing item: %1).").arg(name));
00664         return false;
00665     }
00666     value = attr.value();
00667     return true;
00668 }
00669 
00670 Score ManagerPrivate::readScore(uint i) const
00671 {
00672     Score score(Won);
00673     _scoreInfos->read(i, score);
00674     return score;
00675 }
00676 
00677 int ManagerPrivate::rank(const Score &score) const
00678 {
00679     uint nb = _scoreInfos->nbEntries();
00680     uint i = 0;
00681     for (; i<nb; i++)
00682         if ( readScore(i)<score ) break;
00683     return (i<_scoreInfos->maxNbEntries() ? (int)i : -1);
00684 }
00685 
00686 bool ManagerPrivate::modifySettings(const QString &newName,
00687                                     const QString &comment, bool WWEnabled,
00688                                     QWidget *widget)
00689 {
00690     QString newKey;
00691     bool newPlayer = false;
00692 
00693     if (WWEnabled) {
00694         newPlayer = _playerInfos->key().isEmpty()
00695                     || _playerInfos->registeredName().isEmpty();
00696         KURL url = queryURL((newPlayer ? Register : Change), newName);
00697         Manager::addToQueryURL(url, "comment", comment);
00698 
00699         QDomNamedNodeMap map;
00700         bool ok = doQuery(url, widget, &map);
00701         if ( !ok || (newPlayer && !getFromQuery(map, "key", newKey, widget)) )
00702             return false;
00703     }
00704 
00705     bool ok = _hsConfig->lockForWriting(widget); // no GUI when locking
00706     if (ok) {
00707         // check again name in case the config file has been changed...
00708         // if it has, it is unfortunate because the WWW name is already
00709         // committed but should be very rare and not really problematic
00710         ok = ( !_playerInfos->isNameUsed(newName) );
00711         if (ok)
00712             _playerInfos->modifySettings(newName, comment, WWEnabled, newKey);
00713         _hsConfig->writeAndUnlock();
00714     }
00715     return ok;
00716 }
00717 
00718 void ManagerPrivate::convertToGlobal()
00719 {
00720     // read old highscores
00721     KHighscore *tmp = _hsConfig;
00722     _hsConfig = new KHighscore(true, 0);
00723     QValueVector<Score> scores(_scoreInfos->nbEntries());
00724     for (uint i=0; i<scores.count(); i++)
00725         scores[i] = readScore(i);
00726 
00727     // commit them
00728     delete _hsConfig;
00729     _hsConfig = tmp;
00730     _hsConfig->lockForWriting();
00731     for (uint i=0; i<scores.count(); i++)
00732         if ( scores[i].data("id").toUInt()==_playerInfos->oldLocalId()+1 )
00733             submitLocal(scores[i]);
00734     _hsConfig->writeAndUnlock();
00735 }
00736 
00737 void ManagerPrivate::setGameType(uint type)
00738 {
00739     if (_first) {
00740         _first = false;
00741         if ( _playerInfos->isNewPlayer() ) {
00742             // convert legacy highscores
00743             for (uint i=0; i<_nbGameTypes; i++) {
00744                 setGameType(i);
00745                 manager.convertLegacy(i);
00746             }
00747 
00748 #ifdef HIGHSCORE_DIRECTORY
00749             if ( _playerInfos->isOldLocalPlayer() ) {
00750                 // convert local to global highscores
00751                 for (uint i=0; i<_nbGameTypes; i++) {
00752                     setGameType(i);
00753                     convertToGlobal();
00754                 }
00755             }
00756 #endif
00757         }
00758     }
00759 
00760     Q_ASSERT( type<_nbGameTypes );
00761     _gameType = kMin(type, _nbGameTypes-1);
00762     QString str = "scores";
00763     QString lab = manager.gameTypeLabel(_gameType, Manager::Standard);
00764     if ( !lab.isEmpty() ) {
00765         _playerInfos->setSubGroup(lab);
00766         str += "_" + lab;
00767     }
00768     _scoreInfos->setGroup(str);
00769 }
00770 
00771 void ManagerPrivate::checkFirst()
00772 {
00773     if (_first) setGameType(0);
00774 }
00775 
00776 int ManagerPrivate::submitScore(const Score &ascore,
00777                                 QWidget *widget, bool askIfAnonymous)
00778 {
00779     checkFirst();
00780 
00781     Score score = ascore;
00782     score.setData("id", _playerInfos->id() + 1);
00783     score.setData("date", QDateTime::currentDateTime());
00784 
00785     // ask new name if anonymous and winner
00786     const char *dontAskAgainName = "highscore_ask_name_dialog";
00787     QString newName;
00788     KMessageBox::ButtonCode dummy;
00789     if ( score.type()==Won && askIfAnonymous && _playerInfos->isAnonymous()
00790      && KMessageBox::shouldBeShownYesNo(dontAskAgainName, dummy) ) {
00791          AskNameDialog d(widget);
00792          if ( d.exec()==QDialog::Accepted ) newName = d.name();
00793          if ( d.dontAskAgain() )
00794              KMessageBox::saveDontShowAgainYesNo(dontAskAgainName,
00795                                                  KMessageBox::No);
00796     }
00797 
00798     int rank = -1;
00799     if ( _hsConfig->lockForWriting(widget) ) { // no GUI when locking
00800         // check again new name in case the config file has been changed...
00801         if ( !newName.isEmpty() && !_playerInfos->isNameUsed(newName) )
00802              _playerInfos->modifyName(newName);
00803 
00804         // commit locally
00805         _playerInfos->submitScore(score);
00806         if ( score.type()==Won ) rank = submitLocal(score);
00807         _hsConfig->writeAndUnlock();
00808     }
00809 
00810     if ( _playerInfos->isWWEnabled() )
00811         submitWorldWide(score, widget);
00812 
00813     return rank;
00814 }
00815 
00816 int ManagerPrivate::submitLocal(const Score &score)
00817 {
00818     int r = rank(score);
00819     if ( r!=-1 ) {
00820         uint nb = _scoreInfos->nbEntries();
00821         if ( nb<_scoreInfos->maxNbEntries() ) nb++;
00822         _scoreInfos->write(r, score, nb);
00823     }
00824     return r;
00825 }
00826 
00827 bool ManagerPrivate::submitWorldWide(const Score &score,
00828                                      QWidget *widget) const
00829 {
00830     if ( score.type()==Lost && !trackLostGames ) return true;
00831     if ( score.type()==Draw && !trackDrawGames ) return true;
00832 
00833     KURL url = queryURL(Submit);
00834     manager.additionalQueryItems(url, score);
00835     int s = (score.type()==Won ? score.score() : (int)score.type());
00836     QString str =  QString::number(s);
00837     Manager::addToQueryURL(url, "score", str);
00838     KMD5 context(QString(_playerInfos->registeredName() + str).latin1());
00839     Manager::addToQueryURL(url, "check", context.hexDigest());
00840 
00841     return doQuery(url, widget);
00842 }
00843 
00844 void ManagerPrivate::exportHighscores(QTextStream &s)
00845 {
00846     uint tmp = _gameType;
00847 
00848     for (uint i=0; i<_nbGameTypes; i++) {
00849         setGameType(i);
00850         if ( _nbGameTypes>1 ) {
00851             if ( i!=0 ) s << endl;
00852             s << "--------------------------------" << endl;
00853             s << "Game type: "
00854               << manager.gameTypeLabel(_gameType, Manager::I18N)
00855               << endl;
00856             s << endl;
00857         }
00858         s << "Players list:" << endl;
00859         _playerInfos->exportToText(s);
00860         s << endl;
00861         s << "Highscores list:" << endl;
00862         _scoreInfos->exportToText(s);
00863     }
00864 
00865     setGameType(tmp);
00866 }
00867 
00868 } // namespace
KDE Logo
This file is part of the documentation for libkdegames Library Version 3.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Sep 12 05:17:50 2005 by doxygen 1.4.4 written by Dimitri van Heesch, © 1997-2003