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"