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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Aug 26 00:21:39 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003