|
|
|
/*
|
|
|
|
This file is part of the KDE games library
|
|
|
|
Copyright (C) 2001-2004 Nicolas Hadacek (hadacek@kde.org)
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "kexthighscore_internal.h"
|
|
|
|
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqlayout.h>
|
|
|
|
#include <tqdom.h>
|
|
|
|
|
|
|
|
#include <kglobal.h>
|
|
|
|
#include <kio/netaccess.h>
|
|
|
|
#include <kio/job.h>
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
#include <kmdcodec.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "kexthighscore.h"
|
|
|
|
#include "kexthighscore_gui.h"
|
|
|
|
#include "kemailsettings.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace KExtHighscore
|
|
|
|
{
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const char ItemContainer::ANONYMOUS[] = "_";
|
|
|
|
const char ItemContainer::ANONYMOUS_LABEL[] = I18N_NOOP("anonymous");
|
|
|
|
|
|
|
|
ItemContainer::ItemContainer()
|
|
|
|
: _item(0)
|
|
|
|
{}
|
|
|
|
|
|
|
|
ItemContainer::~ItemContainer()
|
|
|
|
{
|
|
|
|
delete _item;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemContainer::setItem(Item *item)
|
|
|
|
{
|
|
|
|
delete _item;
|
|
|
|
_item = item;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ItemContainer::entryName() const
|
|
|
|
{
|
|
|
|
if ( _subGroup.isEmpty() ) return _name;
|
|
|
|
return _name + "_" + _subGroup;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQVariant ItemContainer::read(uint i) const
|
|
|
|
{
|
|
|
|
Q_ASSERT(_item);
|
|
|
|
|
|
|
|
TQVariant v = _item->defaultValue();
|
|
|
|
if ( isStored() ) {
|
|
|
|
internal->hsConfig().setHighscoreGroup(_group);
|
|
|
|
v = internal->hsConfig().readPropertyEntry(i+1, entryName(), v);
|
|
|
|
}
|
|
|
|
return _item->read(i, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ItemContainer::pretty(uint i) const
|
|
|
|
{
|
|
|
|
Q_ASSERT(_item);
|
|
|
|
return _item->pretty(i, read(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemContainer::write(uint i, const TQVariant &value) const
|
|
|
|
{
|
|
|
|
Q_ASSERT( isStored() );
|
|
|
|
Q_ASSERT( internal->hsConfig().isLocked() );
|
|
|
|
internal->hsConfig().setHighscoreGroup(_group);
|
|
|
|
internal->hsConfig().writeEntry(i+1, entryName(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint ItemContainer::increment(uint i) const
|
|
|
|
{
|
|
|
|
uint v = read(i).toUInt() + 1;
|
|
|
|
write(i, v);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
ItemArray::ItemArray()
|
|
|
|
: _group(""), _subGroup("") // no null groups
|
|
|
|
{}
|
|
|
|
|
|
|
|
ItemArray::~ItemArray()
|
|
|
|
{
|
|
|
|
for (uint i=0; i<size(); i++) delete at(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ItemArray::findIndex(const TQString &name) const
|
|
|
|
{
|
|
|
|
for (uint i=0; i<size(); i++)
|
|
|
|
if ( at(i)->name()==name ) return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ItemContainer *ItemArray::item(const TQString &name) const
|
|
|
|
{
|
|
|
|
int i = findIndex(name);
|
|
|
|
if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
|
|
|
|
<< "\"" << endl;
|
|
|
|
return at(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
ItemContainer *ItemArray::item(const TQString &name)
|
|
|
|
{
|
|
|
|
int i = findIndex(name);
|
|
|
|
if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
|
|
|
|
<< "\"" << endl;
|
|
|
|
return at(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemArray::setItem(const TQString &name, Item *item)
|
|
|
|
{
|
|
|
|
int i = findIndex(name);
|
|
|
|
if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name
|
|
|
|
<< "\"" << endl;
|
|
|
|
bool stored = at(i)->isStored();
|
|
|
|
bool canHaveSubGroup = at(i)->canHaveSubGroup();
|
|
|
|
_setItem(i, name, item, stored, canHaveSubGroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemArray::addItem(const TQString &name, Item *item,
|
|
|
|
bool stored, bool canHaveSubGroup)
|
|
|
|
{
|
|
|
|
if ( findIndex(name)!=-1 )
|
|
|
|
kdError(11002) << "item already exists \"" << name << "\"" << endl;
|
|
|
|
uint i = size();
|
|
|
|
resize(i+1);
|
|
|
|
at(i) = new ItemContainer;
|
|
|
|
_setItem(i, name, item, stored, canHaveSubGroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemArray::_setItem(uint i, const TQString &name, Item *item,
|
|
|
|
bool stored, bool canHaveSubGroup)
|
|
|
|
{
|
|
|
|
at(i)->setItem(item);
|
|
|
|
at(i)->setName(name);
|
|
|
|
at(i)->setGroup(stored ? _group : TQString());
|
|
|
|
at(i)->setSubGroup(canHaveSubGroup ? _subGroup : TQString());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemArray::setGroup(const TQString &group)
|
|
|
|
{
|
|
|
|
Q_ASSERT( !group.isNull() );
|
|
|
|
_group = group;
|
|
|
|
for (uint i=0; i<size(); i++)
|
|
|
|
if ( at(i)->isStored() ) at(i)->setGroup(group);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemArray::setSubGroup(const TQString &subGroup)
|
|
|
|
{
|
|
|
|
Q_ASSERT( !subGroup.isNull() );
|
|
|
|
_subGroup = subGroup;
|
|
|
|
for (uint i=0; i<size(); i++)
|
|
|
|
if ( at(i)->canHaveSubGroup() ) at(i)->setSubGroup(subGroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemArray::read(uint k, Score &data) const
|
|
|
|
{
|
|
|
|
for (uint i=0; i<size(); i++) {
|
|
|
|
if ( !at(i)->isStored() ) continue;
|
|
|
|
data.setData(at(i)->name(), at(i)->read(k));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemArray::write(uint k, const Score &data, uint nb) const
|
|
|
|
{
|
|
|
|
for (uint i=0; i<size(); i++) {
|
|
|
|
if ( !at(i)->isStored() ) continue;
|
|
|
|
for (uint j=nb-1; j>k; j--) at(i)->write(j, at(i)->read(j-1));
|
|
|
|
at(i)->write(k, data.data(at(i)->name()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemArray::exportToText(TQTextStream &s) const
|
|
|
|
{
|
|
|
|
for (uint k=0; k<nbEntries()+1; k++) {
|
|
|
|
for (uint i=0; i<size(); i++) {
|
|
|
|
const Item *item = at(i)->item();
|
|
|
|
if ( item->isVisible() ) {
|
|
|
|
if ( i!=0 ) s << '\t';
|
|
|
|
if ( k==0 ) s << item->label();
|
|
|
|
else s << at(i)->pretty(k-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class ScoreNameItem : public NameItem
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ScoreNameItem(const ScoreInfos &score, const PlayerInfos &infos)
|
|
|
|
: _score(score), _infos(infos) {}
|
|
|
|
|
|
|
|
TQString pretty(uint i, const TQVariant &v) const {
|
|
|
|
uint id = _score.item("id")->read(i).toUInt();
|
|
|
|
if ( id==0 ) return NameItem::pretty(i, v);
|
|
|
|
return _infos.prettyName(id-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const ScoreInfos &_score;
|
|
|
|
const PlayerInfos &_infos;
|
|
|
|
};
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
ScoreInfos::ScoreInfos(uint maxNbEntries, const PlayerInfos &infos)
|
|
|
|
: _maxNbEntries(maxNbEntries)
|
|
|
|
{
|
|
|
|
addItem("id", new Item((uint)0));
|
|
|
|
addItem("rank", new RankItem, false);
|
|
|
|
addItem("name", new ScoreNameItem(*this, infos));
|
|
|
|
addItem("score", Manager::createItem(Manager::ScoreDefault));
|
|
|
|
addItem("date", new DateItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint ScoreInfos::nbEntries() const
|
|
|
|
{
|
|
|
|
uint i = 0;
|
|
|
|
for (; i<_maxNbEntries; i++)
|
|
|
|
if ( item("score")->read(i)==item("score")->item()->defaultValue() )
|
|
|
|
break;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const char *HS_ID = "player id";
|
|
|
|
const char *HS_REGISTERED_NAME = "registered name";
|
|
|
|
const char *HS_KEY = "player key";
|
|
|
|
const char *HS_WW_ENABLED = "ww hs enabled";
|
|
|
|
|
|
|
|
PlayerInfos::PlayerInfos()
|
|
|
|
{
|
|
|
|
setGroup("players");
|
|
|
|
|
|
|
|
// standard items
|
|
|
|
addItem("name", new NameItem);
|
|
|
|
Item *it = new Item((uint)0, i18n("Games Count"),TQt::AlignRight);
|
|
|
|
addItem("nb games", it, true, true);
|
|
|
|
it = Manager::createItem(Manager::MeanScoreDefault);
|
|
|
|
addItem("mean score", it, true, true);
|
|
|
|
it = Manager::createItem(Manager::BestScoreDefault);
|
|
|
|
addItem("best score", it, true, true);
|
|
|
|
addItem("date", new DateItem, true, true);
|
|
|
|
it = new Item(TQString(), i18n("Comment"), TQt::AlignLeft);
|
|
|
|
addItem("comment", it);
|
|
|
|
|
|
|
|
// statistics items
|
|
|
|
addItem("nb black marks", new Item((uint)0), true, true); // legacy
|
|
|
|
addItem("nb lost games", new Item((uint)0), true, true);
|
|
|
|
addItem("nb draw games", new Item((uint)0), true, true);
|
|
|
|
addItem("current trend", new Item((int)0), true, true);
|
|
|
|
addItem("max lost trend", new Item((uint)0), true, true);
|
|
|
|
addItem("max won trend", new Item((uint)0), true, true);
|
|
|
|
|
|
|
|
struct passwd *pwd = getpwuid(getuid());
|
|
|
|
TQString username = pwd->pw_name;
|
|
|
|
#ifdef HIGHSCORE_DIRECTORY
|
|
|
|
internal->hsConfig().setHighscoreGroup("players");
|
|
|
|
for (uint i=0; ;i++) {
|
|
|
|
if ( !internal->hsConfig().hasEntry(i+1, "username") ) {
|
|
|
|
_newPlayer = true;
|
|
|
|
_id = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ( internal->hsConfig().readEntry(i+1, "username")==username ) {
|
|
|
|
_newPlayer = false;
|
|
|
|
_id = i;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
internal->hsConfig().lockForWriting();
|
|
|
|
KEMailSettings emailConfig;
|
|
|
|
emailConfig.setProfile(emailConfig.defaultProfileName());
|
|
|
|
TQString name = emailConfig.getSetting(KEMailSettings::RealName);
|
|
|
|
if ( name.isEmpty() || isNameUsed(name) ) name = username;
|
|
|
|
if ( isNameUsed(name) ) name= TQString(ItemContainer::ANONYMOUS);
|
|
|
|
#ifdef HIGHSCORE_DIRECTORY
|
|
|
|
internal->hsConfig().writeEntry(_id+1, "username", username);
|
|
|
|
item("name")->write(_id, name);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ConfigGroup cg;
|
|
|
|
_oldLocalPlayer = cg.config()->hasKey(HS_ID);
|
|
|
|
_oldLocalId = cg.config()->readUnsignedNumEntry(HS_ID);
|
|
|
|
#ifdef HIGHSCORE_DIRECTORY
|
|
|
|
if (_oldLocalPlayer) { // player already exists in local config file
|
|
|
|
// copy player data
|
|
|
|
TQString prefix = TQString("%1_").arg(_oldLocalId+1);
|
|
|
|
TQMap<TQString, TQString> entries =
|
|
|
|
cg.config()->entryMap("KHighscore_players");
|
|
|
|
TQMap<TQString, TQString>::const_iterator it;
|
|
|
|
for (it=entries.begin(); it!=entries.end(); ++it) {
|
|
|
|
TQString key = it.key();
|
|
|
|
if ( key.find(prefix)==0 ) {
|
|
|
|
TQString name = key.right(key.length()-prefix.length());
|
|
|
|
if ( name!="name" || !isNameUsed(it.data()) )
|
|
|
|
internal->hsConfig().writeEntry(_id+1, name, it.data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
_newPlayer = !_oldLocalPlayer;
|
|
|
|
if (_oldLocalPlayer) _id = _oldLocalId;
|
|
|
|
else {
|
|
|
|
_id = nbEntries();
|
|
|
|
cg.config()->writeEntry(HS_ID, _id);
|
|
|
|
item("name")->write(_id, name);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
_bound = true;
|
|
|
|
internal->hsConfig().writeAndUnlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayerInfos::createHistoItems(const TQMemArray<uint> &scores, bool bound)
|
|
|
|
{
|
|
|
|
Q_ASSERT( _histogram.size()==0 );
|
|
|
|
_bound = bound;
|
|
|
|
_histogram = scores;
|
|
|
|
for (uint i=1; i<histoSize(); i++)
|
|
|
|
addItem(histoName(i), new Item((uint)0), true, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PlayerInfos::isAnonymous() const
|
|
|
|
{
|
|
|
|
return ( name()==ItemContainer::ANONYMOUS );
|
|
|
|
}
|
|
|
|
|
|
|
|
uint PlayerInfos::nbEntries() const
|
|
|
|
{
|
|
|
|
internal->hsConfig().setHighscoreGroup("players");
|
|
|
|
TQStringList list = internal->hsConfig().readList("name", -1);
|
|
|
|
return list.count();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString PlayerInfos::key() const
|
|
|
|
{
|
|
|
|
ConfigGroup cg;
|
|
|
|
return cg.config()->readEntry(HS_KEY, TQString());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PlayerInfos::isWWEnabled() const
|
|
|
|
{
|
|
|
|
ConfigGroup cg;
|
|
|
|
return cg.config()->readBoolEntry(HS_WW_ENABLED, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString PlayerInfos::histoName(uint i) const
|
|
|
|
{
|
|
|
|
const TQMemArray<uint> &sh = _histogram;
|
|
|
|
Q_ASSERT( i<sh.size() || (_bound || i==sh.size()) );
|
|
|
|
if ( i==sh.size() )
|
|
|
|
return TQString("nb scores greater than %1").arg(sh[sh.size()-1]);
|
|
|
|
return TQString("nb scores less than %1").arg(sh[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint PlayerInfos::histoSize() const
|
|
|
|
{
|
|
|
|
return _histogram.size() + (_bound ? 0 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayerInfos::submitScore(const Score &score) const
|
|
|
|
{
|
|
|
|
// update counts
|
|
|
|
uint nbGames = item("nb games")->increment(_id);
|
|
|
|
switch (score.type()) {
|
|
|
|
case Lost:
|
|
|
|
item("nb lost games")->increment(_id);
|
|
|
|
break;
|
|
|
|
case Won: break;
|
|
|
|
case Draw:
|
|
|
|
item("nb draw games")->increment(_id);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
// update mean
|
|
|
|
if ( score.type()==Won ) {
|
|
|
|
uint nbWonGames = nbGames - item("nb lost games")->read(_id).toUInt()
|
|
|
|
- item("nb draw games")->read(_id).toUInt()
|
|
|
|
- item("nb black marks")->read(_id).toUInt(); // legacy
|
|
|
|
double mean = (nbWonGames==1 ? 0.0
|
|
|
|
: item("mean score")->read(_id).toDouble());
|
|
|
|
mean += (double(score.score()) - mean) / nbWonGames;
|
|
|
|
item("mean score")->write(_id, mean);
|
|
|
|
}
|
|
|
|
|
|
|
|
// update best score
|
|
|
|
Score best = score; // copy optionnal fields (there are not taken into account here)
|
|
|
|
best.setScore( item("best score")->read(_id).toUInt() );
|
|
|
|
if ( best<score ) {
|
|
|
|
item("best score")->write(_id, score.score());
|
|
|
|
item("date")->write(_id, score.data("date").toDateTime());
|
|
|
|
}
|
|
|
|
|
|
|
|
// update trends
|
|
|
|
int current = item("current trend")->read(_id).toInt();
|
|
|
|
switch (score.type()) {
|
|
|
|
case Won: {
|
|
|
|
if ( current<0 ) current = 0;
|
|
|
|
current++;
|
|
|
|
uint won = item("max won trend")->read(_id).toUInt();
|
|
|
|
if ( (uint)current>won ) item("max won trend")->write(_id, current);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Lost: {
|
|
|
|
if ( current>0 ) current = 0;
|
|
|
|
current--;
|
|
|
|
uint lost = item("max lost trend")->read(_id).toUInt();
|
|
|
|
uint clost = -current;
|
|
|
|
if ( clost>lost ) item("max lost trend")->write(_id, clost);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Draw:
|
|
|
|
current = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item("current trend")->write(_id, current);
|
|
|
|
|
|
|
|
// update histogram
|
|
|
|
if ( score.type()==Won ) {
|
|
|
|
const TQMemArray<uint> &sh = _histogram;
|
|
|
|
for (uint i=1; i<histoSize(); i++)
|
|
|
|
if ( i==sh.size() || score.score()<sh[i] ) {
|
|
|
|
item(histoName(i))->increment(_id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PlayerInfos::isNameUsed(const TQString &newName) const
|
|
|
|
{
|
|
|
|
if ( newName==name() ) return false; // own name...
|
|
|
|
for (uint i=0; i<nbEntries(); i++)
|
|
|
|
if ( newName.lower()==item("name")->read(i).toString().lower() ) return true;
|
|
|
|
if ( newName==i18n(ItemContainer::ANONYMOUS_LABEL) ) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayerInfos::modifyName(const TQString &newName) const
|
|
|
|
{
|
|
|
|
item("name")->write(_id, newName);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayerInfos::modifySettings(const TQString &newName,
|
|
|
|
const TQString &comment, bool WWEnabled,
|
|
|
|
const TQString &newKey) const
|
|
|
|
{
|
|
|
|
modifyName(newName);
|
|
|
|
item("comment")->write(_id, comment);
|
|
|
|
ConfigGroup cg;
|
|
|
|
cg.config()->writeEntry(HS_WW_ENABLED, WWEnabled);
|
|
|
|
if ( !newKey.isEmpty() ) cg.config()->writeEntry(HS_KEY, newKey);
|
|
|
|
if (WWEnabled) cg.config()->writeEntry(HS_REGISTERED_NAME, newName);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString PlayerInfos::registeredName() const
|
|
|
|
{
|
|
|
|
ConfigGroup cg;
|
|
|
|
return cg.config()->readEntry(HS_REGISTERED_NAME, TQString());
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayerInfos::removeKey()
|
|
|
|
{
|
|
|
|
ConfigGroup cg;
|
|
|
|
|
|
|
|
// save old key/nickname
|
|
|
|
uint i = 0;
|
|
|
|
TQString str = "%1 old #%2";
|
|
|
|
TQString sk;
|
|
|
|
do {
|
|
|
|
i++;
|
|
|
|
sk = str.arg(HS_KEY).arg(i);
|
|
|
|
} while ( !cg.config()->readEntry(sk, TQString()).isEmpty() );
|
|
|
|
cg.config()->writeEntry(sk, key());
|
|
|
|
cg.config()->writeEntry(str.arg(HS_REGISTERED_NAME).arg(i),
|
|
|
|
registeredName());
|
|
|
|
|
|
|
|
// clear current key/nickname
|
|
|
|
cg.config()->deleteEntry(HS_KEY);
|
|
|
|
cg.config()->deleteEntry(HS_REGISTERED_NAME);
|
|
|
|
cg.config()->writeEntry(HS_WW_ENABLED, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
ManagerPrivate::ManagerPrivate(uint nbGameTypes, Manager &m)
|
|
|
|
: manager(m), showStatistics(false), showDrawGames(false),
|
|
|
|
trackLostGames(false), trackDrawGames(false),
|
|
|
|
showMode(Manager::ShowForHigherScore),
|
|
|
|
_first(true), _nbGameTypes(nbGameTypes), _gameType(0)
|
|
|
|
{}
|
|
|
|
|
|
|
|
void ManagerPrivate::init(uint maxNbEntries)
|
|
|
|
{
|
|
|
|
_hsConfig = new KHighscore(false, 0);
|
|
|
|
_playerInfos = new PlayerInfos;
|
|
|
|
_scoreInfos = new ScoreInfos(maxNbEntries, *_playerInfos);
|
|
|
|
}
|
|
|
|
|
|
|
|
ManagerPrivate::~ManagerPrivate()
|
|
|
|
{
|
|
|
|
delete _scoreInfos;
|
|
|
|
delete _playerInfos;
|
|
|
|
delete _hsConfig;
|
|
|
|
}
|
|
|
|
|
|
|
|
KURL ManagerPrivate::queryURL(QueryType type, const TQString &newName) const
|
|
|
|
{
|
|
|
|
KURL url = serverURL;
|
|
|
|
TQString nameItem = "nickname";
|
|
|
|
TQString name = _playerInfos->registeredName();
|
|
|
|
bool withVersion = true;
|
|
|
|
bool key = false;
|
|
|
|
bool level = false;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case Submit:
|
|
|
|
url.addPath("submit.php");
|
|
|
|
level = true;
|
|
|
|
key = true;
|
|
|
|
break;
|
|
|
|
case Register:
|
|
|
|
url.addPath("register.php");
|
|
|
|
name = newName;
|
|
|
|
break;
|
|
|
|
case Change:
|
|
|
|
url.addPath("change.php");
|
|
|
|
key = true;
|
|
|
|
if ( newName!=name )
|
|
|
|
Manager::addToQueryURL(url, "new_nickname", newName);
|
|
|
|
break;
|
|
|
|
case Players:
|
|
|
|
url.addPath("players.php");
|
|
|
|
nameItem = "highlight";
|
|
|
|
withVersion = false;
|
|
|
|
break;
|
|
|
|
case Scores:
|
|
|
|
url.addPath("highscores.php");
|
|
|
|
withVersion = false;
|
|
|
|
if ( _nbGameTypes>1 ) level = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (withVersion) Manager::addToQueryURL(url, "version", version);
|
|
|
|
if ( !name.isEmpty() ) Manager::addToQueryURL(url, nameItem, name);
|
|
|
|
if (key) Manager::addToQueryURL(url, "key", _playerInfos->key());
|
|
|
|
if (level) {
|
|
|
|
TQString label = manager.gameTypeLabel(_gameType, Manager::WW);
|
|
|
|
if ( !label.isEmpty() ) Manager::addToQueryURL(url, "level", label);
|
|
|
|
}
|
|
|
|
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
// strings that needs to be translated (coming from the highscores server)
|
|
|
|
const char *DUMMY_STRINGS[] = {
|
|
|
|
I18N_NOOP("Undefined error."),
|
|
|
|
I18N_NOOP("Missing argument(s)."),
|
|
|
|
I18N_NOOP("Invalid argument(s)."),
|
|
|
|
|
|
|
|
I18N_NOOP("Unable to connect to MySQL server."),
|
|
|
|
I18N_NOOP("Unable to select database."),
|
|
|
|
I18N_NOOP("Error on database query."),
|
|
|
|
I18N_NOOP("Error on database insert."),
|
|
|
|
|
|
|
|
I18N_NOOP("Nickname already registered."),
|
|
|
|
I18N_NOOP("Nickname not registered."),
|
|
|
|
I18N_NOOP("Invalid key."),
|
|
|
|
I18N_NOOP("Invalid submit key."),
|
|
|
|
|
|
|
|
I18N_NOOP("Invalid level."),
|
|
|
|
I18N_NOOP("Invalid score.")
|
|
|
|
};
|
|
|
|
|
|
|
|
const char *UNABLE_TO_CONTACT =
|
|
|
|
I18N_NOOP("Unable to contact world-wide highscore server");
|
|
|
|
|
|
|
|
bool ManagerPrivate::doQuery(const KURL &url, TQWidget *parent,
|
|
|
|
TQDomNamedNodeMap *map)
|
|
|
|
{
|
|
|
|
KIO::http_update_cache(url, true, 0); // remove cache !
|
|
|
|
|
|
|
|
TQString tmpFile;
|
|
|
|
if ( !KIO::NetAccess::download(url, tmpFile, parent) ) {
|
|
|
|
TQString details = i18n("Server URL: %1").arg(url.host());
|
|
|
|
KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQFile file(tmpFile);
|
|
|
|
if ( !file.open(IO_ReadOnly) ) {
|
|
|
|
KIO::NetAccess::removeTempFile(tmpFile);
|
|
|
|
TQString details = i18n("Unable to open temporary file.");
|
|
|
|
KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQTextStream t(&file);
|
|
|
|
TQString content = t.read().stripWhiteSpace();
|
|
|
|
file.close();
|
|
|
|
KIO::NetAccess::removeTempFile(tmpFile);
|
|
|
|
|
|
|
|
TQDomDocument doc;
|
|
|
|
if ( doc.setContent(content) ) {
|
|
|
|
TQDomElement root = doc.documentElement();
|
|
|
|
TQDomElement element = root.firstChild().toElement();
|
|
|
|
if ( element.tagName()=="success" ) {
|
|
|
|
if (map) *map = element.attributes();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if ( element.tagName()=="error" ) {
|
|
|
|
TQDomAttr attr = element.attributes().namedItem("label").toAttr();
|
|
|
|
if ( !attr.isNull() ) {
|
|
|
|
TQString msg = i18n(attr.value().latin1());
|
|
|
|
TQString caption = i18n("Message from world-wide highscores "
|
|
|
|
"server");
|
|
|
|
KMessageBox::sorry(parent, msg, caption);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TQString msg = i18n("Invalid answer from world-wide highscores server.");
|
|
|
|
TQString details = i18n("Raw message: %1").arg(content);
|
|
|
|
KMessageBox::detailedSorry(parent, msg, details);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ManagerPrivate::getFromQuery(const TQDomNamedNodeMap &map,
|
|
|
|
const TQString &name, TQString &value,
|
|
|
|
TQWidget *parent)
|
|
|
|
{
|
|
|
|
TQDomAttr attr = map.namedItem(name).toAttr();
|
|
|
|
if ( attr.isNull() ) {
|
|
|
|
KMessageBox::sorry(parent,
|
|
|
|
i18n("Invalid answer from world-wide "
|
|
|
|
"highscores server (missing item: %1).").arg(name));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
value = attr.value();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Score ManagerPrivate::readScore(uint i) const
|
|
|
|
{
|
|
|
|
Score score(Won);
|
|
|
|
_scoreInfos->read(i, score);
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ManagerPrivate::rank(const Score &score) const
|
|
|
|
{
|
|
|
|
uint nb = _scoreInfos->nbEntries();
|
|
|
|
uint i = 0;
|
|
|
|
for (; i<nb; i++)
|
|
|
|
if ( readScore(i)<score ) break;
|
|
|
|
return (i<_scoreInfos->maxNbEntries() ? (int)i : -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ManagerPrivate::modifySettings(const TQString &newName,
|
|
|
|
const TQString &comment, bool WWEnabled,
|
|
|
|
TQWidget *widget)
|
|
|
|
{
|
|
|
|
TQString newKey;
|
|
|
|
bool newPlayer = false;
|
|
|
|
|
|
|
|
if (WWEnabled) {
|
|
|
|
newPlayer = _playerInfos->key().isEmpty()
|
|
|
|
|| _playerInfos->registeredName().isEmpty();
|
|
|
|
KURL url = queryURL((newPlayer ? Register : Change), newName);
|
|
|
|
Manager::addToQueryURL(url, "comment", comment);
|
|
|
|
|
|
|
|
TQDomNamedNodeMap map;
|
|
|
|
bool ok = doQuery(url, widget, &map);
|
|
|
|
if ( !ok || (newPlayer && !getFromQuery(map, "key", newKey, widget)) )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ok = _hsConfig->lockForWriting(widget); // no GUI when locking
|
|
|
|
if (ok) {
|
|
|
|
// check again name in case the config file has been changed...
|
|
|
|
// if it has, it is unfortunate because the WWW name is already
|
|
|
|
// committed but should be very rare and not really problematic
|
|
|
|
ok = ( !_playerInfos->isNameUsed(newName) );
|
|
|
|
if (ok)
|
|
|
|
_playerInfos->modifySettings(newName, comment, WWEnabled, newKey);
|
|
|
|
_hsConfig->writeAndUnlock();
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ManagerPrivate::convertToGlobal()
|
|
|
|
{
|
|
|
|
// read old highscores
|
|
|
|
KHighscore *tmp = _hsConfig;
|
|
|
|
_hsConfig = new KHighscore(true, 0);
|
|
|
|
TQValueVector<Score> scores(_scoreInfos->nbEntries());
|
|
|
|
for (uint i=0; i<scores.count(); i++)
|
|
|
|
scores[i] = readScore(i);
|
|
|
|
|
|
|
|
// commit them
|
|
|
|
delete _hsConfig;
|
|
|
|
_hsConfig = tmp;
|
|
|
|
_hsConfig->lockForWriting();
|
|
|
|
for (uint i=0; i<scores.count(); i++)
|
|
|
|
if ( scores[i].data("id").toUInt()==_playerInfos->oldLocalId()+1 )
|
|
|
|
submitLocal(scores[i]);
|
|
|
|
_hsConfig->writeAndUnlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ManagerPrivate::setGameType(uint type)
|
|
|
|
{
|
|
|
|
if (_first) {
|
|
|
|
_first = false;
|
|
|
|
if ( _playerInfos->isNewPlayer() ) {
|
|
|
|
// convert legacy highscores
|
|
|
|
for (uint i=0; i<_nbGameTypes; i++) {
|
|
|
|
setGameType(i);
|
|
|
|
manager.convertLegacy(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HIGHSCORE_DIRECTORY
|
|
|
|
if ( _playerInfos->isOldLocalPlayer() ) {
|
|
|
|
// convert local to global highscores
|
|
|
|
for (uint i=0; i<_nbGameTypes; i++) {
|
|
|
|
setGameType(i);
|
|
|
|
convertToGlobal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT( type<_nbGameTypes );
|
|
|
|
_gameType = kMin(type, _nbGameTypes-1);
|
|
|
|
TQString str = "scores";
|
|
|
|
TQString lab = manager.gameTypeLabel(_gameType, Manager::Standard);
|
|
|
|
if ( !lab.isEmpty() ) {
|
|
|
|
_playerInfos->setSubGroup(lab);
|
|
|
|
str += "_" + lab;
|
|
|
|
}
|
|
|
|
_scoreInfos->setGroup(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ManagerPrivate::checkFirst()
|
|
|
|
{
|
|
|
|
if (_first) setGameType(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ManagerPrivate::submitScore(const Score &ascore,
|
|
|
|
TQWidget *widget, bool askIfAnonymous)
|
|
|
|
{
|
|
|
|
checkFirst();
|
|
|
|
|
|
|
|
Score score = ascore;
|
|
|
|
score.setData("id", _playerInfos->id() + 1);
|
|
|
|
score.setData("date", TQDateTime::currentDateTime());
|
|
|
|
|
|
|
|
// ask new name if anonymous and winner
|
|
|
|
const char *dontAskAgainName = "highscore_ask_name_dialog";
|
|
|
|
TQString newName;
|
|
|
|
KMessageBox::ButtonCode dummy;
|
|
|
|
if ( score.type()==Won && askIfAnonymous && _playerInfos->isAnonymous()
|
|
|
|
&& KMessageBox::shouldBeShownYesNo(dontAskAgainName, dummy) ) {
|
|
|
|
AskNameDialog d(widget);
|
|
|
|
if ( d.exec()==TQDialog::Accepted ) newName = d.name();
|
|
|
|
if ( d.dontAskAgain() )
|
|
|
|
KMessageBox::saveDontShowAgainYesNo(dontAskAgainName,
|
|
|
|
KMessageBox::No);
|
|
|
|
}
|
|
|
|
|
|
|
|
int rank = -1;
|
|
|
|
if ( _hsConfig->lockForWriting(widget) ) { // no GUI when locking
|
|
|
|
// check again new name in case the config file has been changed...
|
|
|
|
if ( !newName.isEmpty() && !_playerInfos->isNameUsed(newName) )
|
|
|
|
_playerInfos->modifyName(newName);
|
|
|
|
|
|
|
|
// commit locally
|
|
|
|
_playerInfos->submitScore(score);
|
|
|
|
if ( score.type()==Won ) rank = submitLocal(score);
|
|
|
|
_hsConfig->writeAndUnlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( _playerInfos->isWWEnabled() )
|
|
|
|
submitWorldWide(score, widget);
|
|
|
|
|
|
|
|
return rank;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ManagerPrivate::submitLocal(const Score &score)
|
|
|
|
{
|
|
|
|
int r = rank(score);
|
|
|
|
if ( r!=-1 ) {
|
|
|
|
uint nb = _scoreInfos->nbEntries();
|
|
|
|
if ( nb<_scoreInfos->maxNbEntries() ) nb++;
|
|
|
|
_scoreInfos->write(r, score, nb);
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ManagerPrivate::submitWorldWide(const Score &score,
|
|
|
|
TQWidget *widget) const
|
|
|
|
{
|
|
|
|
if ( score.type()==Lost && !trackLostGames ) return true;
|
|
|
|
if ( score.type()==Draw && !trackDrawGames ) return true;
|
|
|
|
|
|
|
|
KURL url = queryURL(Submit);
|
|
|
|
manager.additionalQueryItems(url, score);
|
|
|
|
int s = (score.type()==Won ? score.score() : (int)score.type());
|
|
|
|
TQString str = TQString::number(s);
|
|
|
|
Manager::addToQueryURL(url, "score", str);
|
|
|
|
KMD5 context(TQString(_playerInfos->registeredName() + str).latin1());
|
|
|
|
Manager::addToQueryURL(url, "check", context.hexDigest());
|
|
|
|
|
|
|
|
return doQuery(url, widget);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ManagerPrivate::exportHighscores(TQTextStream &s)
|
|
|
|
{
|
|
|
|
uint tmp = _gameType;
|
|
|
|
|
|
|
|
for (uint i=0; i<_nbGameTypes; i++) {
|
|
|
|
setGameType(i);
|
|
|
|
if ( _nbGameTypes>1 ) {
|
|
|
|
if ( i!=0 ) s << endl;
|
|
|
|
s << "--------------------------------" << endl;
|
|
|
|
s << "Game type: "
|
|
|
|
<< manager.gameTypeLabel(_gameType, Manager::I18N)
|
|
|
|
<< endl;
|
|
|
|
s << endl;
|
|
|
|
}
|
|
|
|
s << "Players list:" << endl;
|
|
|
|
_playerInfos->exportToText(s);
|
|
|
|
s << endl;
|
|
|
|
s << "Highscores list:" << endl;
|
|
|
|
_scoreInfos->exportToText(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
setGameType(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|