File indexing completed on 2024-05-26 04:09:57

0001 /*
0002     This file is part of the KDE games library
0003     SPDX-FileCopyrightText: 2001 Andreas Beckermann <b_mann@gmx.de>
0004     SPDX-FileCopyrightText: 2001 Martin Heni <kde at heni-online.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "kgamepropertyhandler.h"
0010 
0011 // own
0012 #include "kgamemessage.h"
0013 #include <kdegamesprivate_logging.h>
0014 // KF
0015 #include <KLocalizedString>
0016 // Qt
0017 #include <QMap>
0018 #include <QQueue>
0019 
0020 #define KPLAYERHANDLER_LOAD_COOKIE 6239
0021 
0022 //---------------------- KGamePropertyHandler -----------------------------------
0023 class KGamePropertyHandlerPrivate
0024 {
0025 public:
0026     explicit KGamePropertyHandlerPrivate(KGamePropertyHandler *qq)
0027         : q(qq)
0028     {
0029         // qDebug() << ": this=" << q;
0030     }
0031 
0032 public:
0033     KGamePropertyHandler *const q;
0034 
0035     QMap<int, QString> mNameMap;
0036     QMultiHash<int, KGamePropertyBase *> mIdDict;
0037     int mUniqueId = KGamePropertyBase::IdAutomatic;
0038     int mId = 0;
0039     KGamePropertyBase::PropertyPolicy mDefaultPolicy = KGamePropertyBase::PolicyLocal;
0040     bool mDefaultUserspace = true;
0041     int mIndirectEmit = 0;
0042     QQueue<KGamePropertyBase *> mSignalQueue;
0043 };
0044 
0045 KGamePropertyHandler::KGamePropertyHandler(int id, const QObject *receiver, const char *sendf, const char *emitf, QObject *parent)
0046     : QObject(parent)
0047     , d(new KGamePropertyHandlerPrivate(this))
0048 {
0049     registerHandler(id, receiver, sendf, emitf);
0050 }
0051 
0052 KGamePropertyHandler::KGamePropertyHandler(QObject *parent)
0053     : QObject(parent)
0054     , d(new KGamePropertyHandlerPrivate(this))
0055 {
0056 }
0057 
0058 KGamePropertyHandler::~KGamePropertyHandler()
0059 {
0060     // qDebug() ;
0061     clear();
0062     // qDebug() << "done";
0063 }
0064 
0065 int KGamePropertyHandler::id() const
0066 {
0067     return d->mId;
0068 }
0069 
0070 void KGamePropertyHandler::setId(int id)
0071 {
0072     d->mId = id;
0073 }
0074 
0075 void KGamePropertyHandler::registerHandler(int id, const QObject *receiver, const char *sendf, const char *emitf)
0076 {
0077     setId(id);
0078     if (receiver && sendf) {
0079         connect(this, SIGNAL(signalSendMessage(int, QDataStream &, bool *)), receiver, sendf);
0080     }
0081     if (receiver && emitf) {
0082         connect(this, SIGNAL(signalPropertyChanged(KGamePropertyBase *)), receiver, emitf);
0083     }
0084 }
0085 
0086 bool KGamePropertyHandler::processMessage(QDataStream &stream, int id, bool isSender)
0087 {
0088     // qDebug() << ": id=" << id << "mId=" << d->mId;
0089     if (id != d->mId) {
0090         return false; // Is the message meant for us?
0091     }
0092     KGamePropertyBase *p;
0093     int propertyId;
0094     KGameMessage::extractPropertyHeader(stream, propertyId);
0095     // qDebug() << ": Got property" << propertyId;
0096     if (propertyId == KGamePropertyBase::IdCommand) {
0097         int cmd;
0098         KGameMessage::extractPropertyCommand(stream, propertyId, cmd);
0099         // qDebug() << ": Got COMMAND for id= "<<propertyId;
0100         QMultiHash<int, KGamePropertyBase *>::iterator it = d->mIdDict.find(propertyId);
0101         if (it != d->mIdDict.end()) {
0102             p = *it;
0103             if (!isSender || p->policy() == KGamePropertyBase::PolicyClean) {
0104                 p->command(stream, cmd, isSender);
0105             }
0106         } else {
0107             qCCritical(KDEGAMESPRIVATE_LOG) << ": (cmd): property" << propertyId << "not found";
0108         }
0109         return true;
0110     }
0111     QMultiHash<int, KGamePropertyBase *>::iterator it = d->mIdDict.find(propertyId);
0112     if (it != d->mIdDict.end()) {
0113         p = *it;
0114         // qDebug() << ": Loading" << propertyId;
0115         if (!isSender || p->policy() == KGamePropertyBase::PolicyClean) {
0116             p->load(stream);
0117         }
0118     } else {
0119         qCCritical(KDEGAMESPRIVATE_LOG) << ": property" << propertyId << "not found";
0120     }
0121     return true;
0122 }
0123 
0124 bool KGamePropertyHandler::removeProperty(KGamePropertyBase *data)
0125 {
0126     if (!data) {
0127         return false;
0128     }
0129 
0130     d->mNameMap.remove(data->id());
0131     return d->mIdDict.remove(data->id());
0132 }
0133 
0134 bool KGamePropertyHandler::addProperty(KGamePropertyBase *data, const QString &name)
0135 {
0136     // qDebug() << ":" << data->id();
0137     if (d->mIdDict.find(data->id()) != d->mIdDict.end()) {
0138         // this id already exists
0139         qCCritical(KDEGAMESPRIVATE_LOG) << "  -> cannot add property" << data->id();
0140         return false;
0141     } else {
0142         d->mIdDict.insert(data->id(), data);
0143         // if here is a check for "is_debug" or so we can add the strings only in debug mode
0144         // and save memory!!
0145         if (!name.isEmpty()) {
0146             d->mNameMap[data->id()] = name;
0147             // qDebug() << ": nid="<< (data->id()) << "inserted in Map name=" << d->mNameMap[data->id()];
0148             // qDebug() << "Typeid=" << typeid(data).name();
0149             // qDebug() << "Typeid call=" << data->typeinfo()->name();
0150         }
0151     }
0152     return true;
0153 }
0154 
0155 QString KGamePropertyHandler::propertyName(int id) const
0156 {
0157     QString s;
0158     if (d->mIdDict.find(id) != d->mIdDict.end()) {
0159         if (d->mNameMap.contains(id)) {
0160             s = i18n("%1 (%2)", d->mNameMap[id], id);
0161         } else {
0162             s = i18n("Unnamed - ID: %1", id);
0163         }
0164     } else {
0165         // Should _never_ happen
0166         s = i18np("%1 unregistered", "%1 unregistered", id);
0167     }
0168     return s;
0169 }
0170 
0171 bool KGamePropertyHandler::load(QDataStream &stream)
0172 {
0173     // Prevent direct emitting until all is loaded
0174     lockDirectEmit();
0175     uint count, i;
0176     stream >> count;
0177     qDebug() << ":" << count << "KGameProperty objects";
0178     for (i = 0; i < count; ++i) {
0179         processMessage(stream, id(), false);
0180     }
0181     qint16 cookie;
0182     stream >> cookie;
0183     if (cookie == KPLAYERHANDLER_LOAD_COOKIE) {
0184         qDebug() << "   KGamePropertyHandler loaded properly";
0185     } else {
0186         qCCritical(KDEGAMESPRIVATE_LOG) << "KGamePropertyHandler loading error. probably format error";
0187     }
0188     // Allow direct emitting (if no other lock still holds)
0189     unlockDirectEmit();
0190     return true;
0191 }
0192 
0193 bool KGamePropertyHandler::save(QDataStream &stream)
0194 {
0195     qDebug() << ":" << d->mIdDict.count() << "KGameProperty objects";
0196     stream << (uint)d->mIdDict.count();
0197     QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
0198     while (it.hasNext()) {
0199         it.next();
0200         KGamePropertyBase *base = it.value();
0201         if (base) {
0202             KGameMessage::createPropertyHeader(stream, base->id());
0203             base->save(stream);
0204         }
0205     }
0206     stream << (qint16)KPLAYERHANDLER_LOAD_COOKIE;
0207     return true;
0208 }
0209 
0210 KGamePropertyBase::PropertyPolicy KGamePropertyHandler::policy()
0211 {
0212     // qDebug() << ":" << d->mDefaultPolicy;
0213     return d->mDefaultPolicy;
0214 }
0215 void KGamePropertyHandler::setPolicy(KGamePropertyBase::PropertyPolicy p, bool userspace)
0216 {
0217     // qDebug() << ":" << p;
0218     d->mDefaultPolicy = p;
0219     d->mDefaultUserspace = userspace;
0220     QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
0221     while (it.hasNext()) {
0222         it.next();
0223         if (!userspace || it.value()->id() >= KGamePropertyBase::IdUser) {
0224             it.value()->setPolicy((KGamePropertyBase::PropertyPolicy)p);
0225         }
0226     }
0227 }
0228 
0229 void KGamePropertyHandler::unlockProperties()
0230 {
0231     QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
0232     while (it.hasNext()) {
0233         it.next();
0234         it.value()->unlock();
0235     }
0236 }
0237 
0238 void KGamePropertyHandler::lockProperties()
0239 {
0240     QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
0241     while (it.hasNext()) {
0242         it.next();
0243         it.value()->lock();
0244     }
0245 }
0246 
0247 int KGamePropertyHandler::uniquePropertyId()
0248 {
0249     return d->mUniqueId++;
0250 }
0251 
0252 void KGamePropertyHandler::flush()
0253 {
0254     QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
0255     while (it.hasNext()) {
0256         it.next();
0257         if (it.value()->isDirty()) {
0258             it.value()->sendProperty();
0259         }
0260     }
0261 }
0262 
0263 /* Fire all property signal changed which are collected in
0264  * the queque
0265  */
0266 void KGamePropertyHandler::lockDirectEmit()
0267 {
0268     d->mIndirectEmit++;
0269 }
0270 
0271 void KGamePropertyHandler::unlockDirectEmit()
0272 {
0273     // If the flag is <=0 we emit the queued signals
0274     d->mIndirectEmit--;
0275     if (d->mIndirectEmit <= 0) {
0276         while (!d->mSignalQueue.isEmpty()) {
0277             KGamePropertyBase *prop = d->mSignalQueue.dequeue();
0278             //       qDebug() << "emitting signal for" << prop->id();
0279             Q_EMIT signalPropertyChanged(prop);
0280         }
0281     }
0282 }
0283 
0284 void KGamePropertyHandler::emitSignal(KGamePropertyBase *prop)
0285 {
0286     // If the indirect flag is set (load and network transmit)
0287     // we cannot emit the signals directly as it can happened that
0288     // a signal causes an access to a property which is e.g. not
0289     // yet loaded or received
0290 
0291     if (d->mIndirectEmit > 0) {
0292         // Queque the signal
0293         d->mSignalQueue.enqueue(prop);
0294     } else {
0295         // directly emit
0296         Q_EMIT signalPropertyChanged(prop);
0297     }
0298 }
0299 
0300 bool KGamePropertyHandler::sendProperty(QDataStream &s)
0301 {
0302     bool sent = false;
0303     Q_EMIT signalSendMessage(id(), s, &sent);
0304     return sent;
0305 }
0306 
0307 KGamePropertyBase *KGamePropertyHandler::find(int id)
0308 {
0309     if (d->mIdDict.find(id) == d->mIdDict.end())
0310         return nullptr;
0311     return *(d->mIdDict.find(id));
0312 }
0313 
0314 void KGamePropertyHandler::clear()
0315 {
0316     // Note: Hash iterator method 'toFront()' crashes when applied to first item.
0317     // Therefore we get the keys as list first.
0318     const QList<int> list = d->mIdDict.keys();
0319     for (int key : list) {
0320         KGamePropertyBase *p = d->mIdDict.value(key);
0321         p->unregisterData();
0322         if (d->mIdDict.find(p->id()) != d->mIdDict.end()) {
0323             // shouldn't happen - but if mOwner in KGamePropertyBase is NULL
0324             // this might be possible
0325             removeProperty(p);
0326         }
0327     }
0328 }
0329 
0330 QMultiHash<int, KGamePropertyBase *> &KGamePropertyHandler::dict() const
0331 {
0332     return d->mIdDict;
0333 }
0334 
0335 QString KGamePropertyHandler::propertyValue(KGamePropertyBase *prop)
0336 {
0337     if (!prop) {
0338         return i18n("NULL pointer");
0339     }
0340 
0341     QString value;
0342 
0343     const type_info *t = prop->typeinfo();
0344     if (*t == typeid(int)) {
0345         value = QString::number(((KGamePropertyInt *)prop)->value());
0346     } else if (*t == typeid(unsigned int)) {
0347         value = QString::number(((KGamePropertyUInt *)prop)->value());
0348     } else if (*t == typeid(long int)) {
0349         value = QString::number(((KGameProperty<qint64> *)prop)->value());
0350     } else if (*t == typeid(unsigned long int)) {
0351         value = QString::number(((KGameProperty<quint64> *)prop)->value());
0352     } else if (*t == typeid(QString)) {
0353         value = ((KGamePropertyQString *)prop)->value();
0354     } else if (*t == typeid(qint8)) {
0355         value = ((KGamePropertyBool *)prop)->value() ? i18n("True") : i18n("False");
0356     } else {
0357         Q_EMIT signalRequestValue(prop, value);
0358     }
0359 
0360     if (value.isNull()) {
0361         value = i18n("Unknown");
0362     }
0363     return value;
0364 }
0365 
0366 void KGamePropertyHandler::Debug()
0367 {
0368     qDebug() << "-----------------------------------------------------------";
0369     qDebug() << "KGamePropertyHandler:: Debug this=" << this;
0370 
0371     qDebug() << "  Registered properties: (Policy,Lock,Emit,Optimized, Dirty)";
0372     QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
0373     while (it.hasNext()) {
0374         it.next();
0375         KGamePropertyBase *p = it.value();
0376         qDebug() << "  " << p->id() << ": p=" << p->policy() << "l=" << p->isLocked() << "e=" << p->isEmittingSignal() << "o=" << p->isOptimized()
0377                  << "d=" << p->isDirty();
0378     }
0379     qDebug() << "-----------------------------------------------------------";
0380 }
0381 
0382 #include "moc_kgamepropertyhandler.cpp"