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., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, 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 <tqfile.h> 00027 #include <tqlayout.h> 00028 #include <tqdom.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 #include "kemailsettings.h" 00041 00042 00043 namespace KExtHighscore 00044 { 00045 00046 //----------------------------------------------------------------------------- 00047 const char ItemContainer::ANONYMOUS[] = "_"; 00048 const char ItemContainer::ANONYMOUS_LABEL[] = I18N_NOOP("anonymous"); 00049 00050 ItemContainer::ItemContainer() 00051 : _item(0) 00052 {} 00053 00054 ItemContainer::~ItemContainer() 00055 { 00056 delete _item; 00057 } 00058 00059 void ItemContainer::setItem(Item *item) 00060 { 00061 delete _item; 00062 _item = item; 00063 } 00064 00065 TQString ItemContainer::entryName() const 00066 { 00067 if ( _subGroup.isEmpty() ) return _name; 00068 return _name + "_" + _subGroup; 00069 } 00070 00071 TQVariant ItemContainer::read(uint i) const 00072 { 00073 Q_ASSERT(_item); 00074 00075 TQVariant v = _item->defaultValue(); 00076 if ( isStored() ) { 00077 internal->hsConfig().setHighscoreGroup(_group); 00078 v = internal->hsConfig().readPropertyEntry(i+1, entryName(), v); 00079 } 00080 return _item->read(i, v); 00081 } 00082 00083 TQString ItemContainer::pretty(uint i) const 00084 { 00085 Q_ASSERT(_item); 00086 return _item->pretty(i, read(i)); 00087 } 00088 00089 void ItemContainer::write(uint i, const TQVariant &value) const 00090 { 00091 Q_ASSERT( isStored() ); 00092 Q_ASSERT( internal->hsConfig().isLocked() ); 00093 internal->hsConfig().setHighscoreGroup(_group); 00094 internal->hsConfig().writeEntry(i+1, entryName(), value); 00095 } 00096 00097 uint ItemContainer::increment(uint i) const 00098 { 00099 uint v = read(i).toUInt() + 1; 00100 write(i, v); 00101 return v; 00102 } 00103 00104 //----------------------------------------------------------------------------- 00105 ItemArray::ItemArray() 00106 : _group(""), _subGroup("") // no null groups 00107 {} 00108 00109 ItemArray::~ItemArray() 00110 { 00111 for (uint i=0; i<size(); i++) delete at(i); 00112 } 00113 00114 int ItemArray::findIndex(const TQString &name) const 00115 { 00116 for (uint i=0; i<size(); i++) 00117 if ( at(i)->name()==name ) return i; 00118 return -1; 00119 } 00120 00121 const ItemContainer *ItemArray::item(const TQString &name) const 00122 { 00123 int i = findIndex(name); 00124 if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name 00125 << "\"" << endl; 00126 return at(i); 00127 } 00128 00129 ItemContainer *ItemArray::item(const TQString &name) 00130 { 00131 int i = findIndex(name); 00132 if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name 00133 << "\"" << endl; 00134 return at(i); 00135 } 00136 00137 void ItemArray::setItem(const TQString &name, Item *item) 00138 { 00139 int i = findIndex(name); 00140 if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name 00141 << "\"" << endl; 00142 bool stored = at(i)->isStored(); 00143 bool canHaveSubGroup = at(i)->canHaveSubGroup(); 00144 _setItem(i, name, item, stored, canHaveSubGroup); 00145 } 00146 00147 void ItemArray::addItem(const TQString &name, Item *item, 00148 bool stored, bool canHaveSubGroup) 00149 { 00150 if ( findIndex(name)!=-1 ) 00151 kdError(11002) << "item already exists \"" << name << "\"" << endl; 00152 uint i = size(); 00153 resize(i+1); 00154 at(i) = new ItemContainer; 00155 _setItem(i, name, item, stored, canHaveSubGroup); 00156 } 00157 00158 void ItemArray::_setItem(uint i, const TQString &name, Item *item, 00159 bool stored, bool canHaveSubGroup) 00160 { 00161 at(i)->setItem(item); 00162 at(i)->setName(name); 00163 at(i)->setGroup(stored ? _group : TQString()); 00164 at(i)->setSubGroup(canHaveSubGroup ? _subGroup : TQString()); 00165 } 00166 00167 void ItemArray::setGroup(const TQString &group) 00168 { 00169 Q_ASSERT( !group.isNull() ); 00170 _group = group; 00171 for (uint i=0; i<size(); i++) 00172 if ( at(i)->isStored() ) at(i)->setGroup(group); 00173 } 00174 00175 void ItemArray::setSubGroup(const TQString &subGroup) 00176 { 00177 Q_ASSERT( !subGroup.isNull() ); 00178 _subGroup = subGroup; 00179 for (uint i=0; i<size(); i++) 00180 if ( at(i)->canHaveSubGroup() ) at(i)->setSubGroup(subGroup); 00181 } 00182 00183 void ItemArray::read(uint k, Score &data) const 00184 { 00185 for (uint i=0; i<size(); i++) { 00186 if ( !at(i)->isStored() ) continue; 00187 data.setData(at(i)->name(), at(i)->read(k)); 00188 } 00189 } 00190 00191 void ItemArray::write(uint k, const Score &data, uint nb) const 00192 { 00193 for (uint i=0; i<size(); i++) { 00194 if ( !at(i)->isStored() ) continue; 00195 for (uint j=nb-1; j>k; j--) at(i)->write(j, at(i)->read(j-1)); 00196 at(i)->write(k, data.data(at(i)->name())); 00197 } 00198 } 00199 00200 void ItemArray::exportToText(TQTextStream &s) const 00201 { 00202 for (uint k=0; k<nbEntries()+1; k++) { 00203 for (uint i=0; i<size(); i++) { 00204 const Item *item = at(i)->item(); 00205 if ( item->isVisible() ) { 00206 if ( i!=0 ) s << '\t'; 00207 if ( k==0 ) s << item->label(); 00208 else s << at(i)->pretty(k-1); 00209 } 00210 } 00211 s << endl; 00212 } 00213 } 00214 00215 //----------------------------------------------------------------------------- 00216 class ScoreNameItem : public NameItem 00217 { 00218 public: 00219 ScoreNameItem(const ScoreInfos &score, const PlayerInfos &infos) 00220 : _score(score), _infos(infos) {} 00221 00222 TQString pretty(uint i, const TQVariant &v) const { 00223 uint id = _score.item("id")->read(i).toUInt(); 00224 if ( id==0 ) return NameItem::pretty(i, v); 00225 return _infos.prettyName(id-1); 00226 } 00227 00228 private: 00229 const ScoreInfos &_score; 00230 const PlayerInfos &_infos; 00231 }; 00232 00233 //----------------------------------------------------------------------------- 00234 ScoreInfos::ScoreInfos(uint maxNbEntries, const PlayerInfos &infos) 00235 : _maxNbEntries(maxNbEntries) 00236 { 00237 addItem("id", new Item((uint)0)); 00238 addItem("rank", new RankItem, false); 00239 addItem("name", new ScoreNameItem(*this, infos)); 00240 addItem("score", Manager::createItem(Manager::ScoreDefault)); 00241 addItem("date", new DateItem); 00242 } 00243 00244 uint ScoreInfos::nbEntries() const 00245 { 00246 uint i = 0; 00247 for (; i<_maxNbEntries; i++) 00248 if ( item("score")->read(i)==item("score")->item()->defaultValue() ) 00249 break; 00250 return i; 00251 } 00252 00253 //----------------------------------------------------------------------------- 00254 const char *HS_ID = "player id"; 00255 const char *HS_REGISTERED_NAME = "registered name"; 00256 const char *HS_KEY = "player key"; 00257 const char *HS_WW_ENABLED = "ww hs enabled"; 00258 00259 PlayerInfos::PlayerInfos() 00260 { 00261 setGroup("players"); 00262 00263 // standard items 00264 addItem("name", new NameItem); 00265 Item *it = new Item((uint)0, i18n("Games Count"),TQt::AlignRight); 00266 addItem("nb games", it, true, true); 00267 it = Manager::createItem(Manager::MeanScoreDefault); 00268 addItem("mean score", it, true, true); 00269 it = Manager::createItem(Manager::BestScoreDefault); 00270 addItem("best score", it, true, true); 00271 addItem("date", new DateItem, true, true); 00272 it = new Item(TQString(), i18n("Comment"), TQt::AlignLeft); 00273 addItem("comment", it); 00274 00275 // statistics items 00276 addItem("nb black marks", new Item((uint)0), true, true); // legacy 00277 addItem("nb lost games", new Item((uint)0), true, true); 00278 addItem("nb draw games", new Item((uint)0), true, true); 00279 addItem("current trend", new Item((int)0), true, true); 00280 addItem("max lost trend", new Item((uint)0), true, true); 00281 addItem("max won trend", new Item((uint)0), true, true); 00282 00283 struct passwd *pwd = getpwuid(getuid()); 00284 TQString username = pwd->pw_name; 00285 #ifdef HIGHSCORE_DIRECTORY 00286 internal->hsConfig().setHighscoreGroup("players"); 00287 for (uint i=0; ;i++) { 00288 if ( !internal->hsConfig().hasEntry(i+1, "username") ) { 00289 _newPlayer = true; 00290 _id = i; 00291 break; 00292 } 00293 if ( internal->hsConfig().readEntry(i+1, "username")==username ) { 00294 _newPlayer = false; 00295 _id = i; 00296 return; 00297 } 00298 } 00299 #endif 00300 internal->hsConfig().lockForWriting(); 00301 KEMailSettings emailConfig; 00302 emailConfig.setProfile(emailConfig.defaultProfileName()); 00303 TQString name = emailConfig.getSetting(KEMailSettings::RealName); 00304 if ( name.isEmpty() || isNameUsed(name) ) name = username; 00305 if ( isNameUsed(name) ) name= TQString(ItemContainer::ANONYMOUS); 00306 #ifdef HIGHSCORE_DIRECTORY 00307 internal->hsConfig().writeEntry(_id+1, "username", username); 00308 item("name")->write(_id, name); 00309 #endif 00310 00311 ConfigGroup cg; 00312 _oldLocalPlayer = cg.config()->hasKey(HS_ID); 00313 _oldLocalId = cg.config()->readUnsignedNumEntry(HS_ID); 00314 #ifdef HIGHSCORE_DIRECTORY 00315 if (_oldLocalPlayer) { // player already exists in local config file 00316 // copy player data 00317 TQString prefix = TQString("%1_").arg(_oldLocalId+1); 00318 TQMap<TQString, TQString> entries = 00319 cg.config()->entryMap("KHighscore_players"); 00320 TQMap<TQString, TQString>::const_iterator it; 00321 for (it=entries.begin(); it!=entries.end(); ++it) { 00322 TQString key = it.key(); 00323 if ( key.find(prefix)==0 ) { 00324 TQString name = key.right(key.length()-prefix.length()); 00325 if ( name!="name" || !isNameUsed(it.data()) ) 00326 internal->hsConfig().writeEntry(_id+1, name, it.data()); 00327 } 00328 } 00329 } 00330 #else 00331 _newPlayer = !_oldLocalPlayer; 00332 if (_oldLocalPlayer) _id = _oldLocalId; 00333 else { 00334 _id = nbEntries(); 00335 cg.config()->writeEntry(HS_ID, _id); 00336 item("name")->write(_id, name); 00337 } 00338 #endif 00339 _bound = true; 00340 internal->hsConfig().writeAndUnlock(); 00341 } 00342 00343 void PlayerInfos::createHistoItems(const TQMemArray<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 TQStringList list = internal->hsConfig().readList("name", -1); 00361 return list.count(); 00362 } 00363 00364 TQString PlayerInfos::key() const 00365 { 00366 ConfigGroup cg; 00367 return cg.config()->readEntry(HS_KEY, TQString()); 00368 } 00369 00370 bool PlayerInfos::isWWEnabled() const 00371 { 00372 ConfigGroup cg; 00373 return cg.config()->readBoolEntry(HS_WW_ENABLED, false); 00374 } 00375 00376 TQString PlayerInfos::histoName(uint i) const 00377 { 00378 const TQMemArray<uint> &sh = _histogram; 00379 Q_ASSERT( i<sh.size() || (_bound || i==sh.size()) ); 00380 if ( i==sh.size() ) 00381 return TQString("nb scores greater than %1").arg(sh[sh.size()-1]); 00382 return TQString("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 TQMemArray<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 TQString &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 TQString &newName) const 00468 { 00469 item("name")->write(_id, newName); 00470 } 00471 00472 void PlayerInfos::modifySettings(const TQString &newName, 00473 const TQString &comment, bool WWEnabled, 00474 const TQString &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 TQString PlayerInfos::registeredName() const 00485 { 00486 ConfigGroup cg; 00487 return cg.config()->readEntry(HS_REGISTERED_NAME, TQString()); 00488 } 00489 00490 void PlayerInfos::removeKey() 00491 { 00492 ConfigGroup cg; 00493 00494 // save old key/nickname 00495 uint i = 0; 00496 TQString str = "%1 old #%2"; 00497 TQString sk; 00498 do { 00499 i++; 00500 sk = str.arg(HS_KEY).arg(i); 00501 } while ( !cg.config()->readEntry(sk, TQString()).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 TQString &newName) const 00535 { 00536 KURL url = serverURL; 00537 TQString nameItem = "nickname"; 00538 TQString 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 TQString 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, TQWidget *parent, 00606 TQDomNamedNodeMap *map) 00607 { 00608 KIO::http_update_cache(url, true, 0); // remove cache ! 00609 00610 TQString tmpFile; 00611 if ( !KIO::NetAccess::download(url, tmpFile, parent) ) { 00612 TQString details = i18n("Server URL: %1").arg(url.host()); 00613 KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details); 00614 return false; 00615 } 00616 00617 TQFile file(tmpFile); 00618 if ( !file.open(IO_ReadOnly) ) { 00619 KIO::NetAccess::removeTempFile(tmpFile); 00620 TQString details = i18n("Unable to open temporary file."); 00621 KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details); 00622 return false; 00623 } 00624 00625 TQTextStream t(&file); 00626 TQString content = t.read().stripWhiteSpace(); 00627 file.close(); 00628 KIO::NetAccess::removeTempFile(tmpFile); 00629 00630 TQDomDocument doc; 00631 if ( doc.setContent(content) ) { 00632 TQDomElement root = doc.documentElement(); 00633 TQDomElement 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 TQDomAttr attr = element.attributes().namedItem("label").toAttr(); 00640 if ( !attr.isNull() ) { 00641 TQString msg = i18n(attr.value().latin1()); 00642 TQString caption = i18n("Message from world-wide highscores " 00643 "server"); 00644 KMessageBox::sorry(parent, msg, caption); 00645 return false; 00646 } 00647 } 00648 } 00649 TQString msg = i18n("Invalid answer from world-wide highscores server."); 00650 TQString details = i18n("Raw message: %1").arg(content); 00651 KMessageBox::detailedSorry(parent, msg, details); 00652 return false; 00653 } 00654 00655 bool ManagerPrivate::getFromQuery(const TQDomNamedNodeMap &map, 00656 const TQString &name, TQString &value, 00657 TQWidget *parent) 00658 { 00659 TQDomAttr 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 TQString &newName, 00687 const TQString &comment, bool WWEnabled, 00688 TQWidget *widget) 00689 { 00690 TQString 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 TQDomNamedNodeMap 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 TQValueVector<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 TQString str = "scores"; 00763 TQString 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 TQWidget *widget, bool askIfAnonymous) 00778 { 00779 checkFirst(); 00780 00781 Score score = ascore; 00782 score.setData("id", _playerInfos->id() + 1); 00783 score.setData("date", TQDateTime::currentDateTime()); 00784 00785 // ask new name if anonymous and winner 00786 const char *dontAskAgainName = "highscore_ask_name_dialog"; 00787 TQString 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()==TQDialog::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 TQWidget *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 TQString str = TQString::number(s); 00837 Manager::addToQueryURL(url, "score", str); 00838 KMD5 context(TQString(_playerInfos->registeredName() + str).latin1()); 00839 Manager::addToQueryURL(url, "check", context.hexDigest()); 00840 00841 return doQuery(url, widget); 00842 } 00843 00844 void ManagerPrivate::exportHighscores(TQTextStream &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