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