KDEGames

kgamepropertyhandler.cpp
1/*
2 This file is part of the KDE games library
3 SPDX-FileCopyrightText: 2001 Andreas Beckermann <b_mann@gmx.de>
4 SPDX-FileCopyrightText: 2001 Martin Heni <kde at heni-online.de>
5
6 SPDX-License-Identifier: LGPL-2.0-only
7*/
8
9#include "kgamepropertyhandler.h"
10
11// own
12#include "kgamemessage.h"
13#include <kdegamesprivate_logging.h>
14// KF
15#include <KLocalizedString>
16// Qt
17#include <QMap>
18#include <QQueue>
19
20#define KPLAYERHANDLER_LOAD_COOKIE 6239
21
22//---------------------- KGamePropertyHandler -----------------------------------
23class KGamePropertyHandlerPrivate
24{
25public:
26 explicit KGamePropertyHandlerPrivate(KGamePropertyHandler *qq)
27 : q(qq)
28 {
29 // qDebug() << ": this=" << q;
30 }
31
32public:
33 KGamePropertyHandler *const q;
34
35 QMap<int, QString> mNameMap;
37 int mUniqueId = KGamePropertyBase::IdAutomatic;
38 int mId = 0;
39 KGamePropertyBase::PropertyPolicy mDefaultPolicy = KGamePropertyBase::PolicyLocal;
40 bool mDefaultUserspace = true;
41 int mIndirectEmit = 0;
42 QQueue<KGamePropertyBase *> mSignalQueue;
43};
44
45KGamePropertyHandler::KGamePropertyHandler(int id, const QObject *receiver, const char *sendf, const char *emitf, QObject *parent)
46 : QObject(parent)
47 , d(new KGamePropertyHandlerPrivate(this))
48{
49 registerHandler(id, receiver, sendf, emitf);
50}
51
53 : QObject(parent)
54 , d(new KGamePropertyHandlerPrivate(this))
55{
56}
57
58KGamePropertyHandler::~KGamePropertyHandler()
59{
60 // qDebug() ;
61 clear();
62 // qDebug() << "done";
63}
64
66{
67 return d->mId;
68}
69
71{
72 d->mId = id;
73}
74
75void KGamePropertyHandler::registerHandler(int id, const QObject *receiver, const char *sendf, const char *emitf)
76{
77 setId(id);
78 if (receiver && sendf) {
79 connect(this, SIGNAL(signalSendMessage(int, QDataStream &, bool *)), receiver, sendf, Qt::DirectConnection);
80 }
81 if (receiver && emitf) {
82 connect(this, SIGNAL(signalPropertyChanged(KGamePropertyBase *)), receiver, emitf);
83 }
84}
85
86bool KGamePropertyHandler::processMessage(QDataStream &stream, int id, bool isSender)
87{
88 // qDebug() << ": id=" << id << "mId=" << d->mId;
89 if (id != d->mId) {
90 return false; // Is the message meant for us?
91 }
93 int propertyId;
94 KGameMessage::extractPropertyHeader(stream, propertyId);
95 // qDebug() << ": Got property" << propertyId;
96 if (propertyId == KGamePropertyBase::IdCommand) {
97 int cmd;
98 KGameMessage::extractPropertyCommand(stream, propertyId, cmd);
99 // qDebug() << ": Got COMMAND for id= "<<propertyId;
100 QMultiHash<int, KGamePropertyBase *>::iterator it = d->mIdDict.find(propertyId);
101 if (it != d->mIdDict.end()) {
102 p = *it;
103 if (!isSender || p->policy() == KGamePropertyBase::PolicyClean) {
104 p->command(stream, cmd, isSender);
105 }
106 } else {
107 qCCritical(KDEGAMESPRIVATE_LOG) << ": (cmd): property" << propertyId << "not found";
108 }
109 return true;
110 }
111 QMultiHash<int, KGamePropertyBase *>::iterator it = d->mIdDict.find(propertyId);
112 if (it != d->mIdDict.end()) {
113 p = *it;
114 // qDebug() << ": Loading" << propertyId;
115 if (!isSender || p->policy() == KGamePropertyBase::PolicyClean) {
116 p->load(stream);
117 }
118 } else {
119 qCCritical(KDEGAMESPRIVATE_LOG) << ": property" << propertyId << "not found";
120 }
121 return true;
122}
123
125{
126 if (!data) {
127 return false;
128 }
129
130 d->mNameMap.remove(data->id());
131 return d->mIdDict.remove(data->id());
132}
133
135{
136 // qDebug() << ":" << data->id();
137 if (d->mIdDict.find(data->id()) != d->mIdDict.end()) {
138 // this id already exists
139 qCCritical(KDEGAMESPRIVATE_LOG) << " -> cannot add property" << data->id();
140 return false;
141 } else {
142 d->mIdDict.insert(data->id(), data);
143 // if here is a check for "is_debug" or so we can add the strings only in debug mode
144 // and save memory!!
145 if (!name.isEmpty()) {
146 d->mNameMap[data->id()] = name;
147 // qDebug() << ": nid="<< (data->id()) << "inserted in Map name=" << d->mNameMap[data->id()];
148 // qDebug() << "Typeid=" << typeid(data).name();
149 // qDebug() << "Typeid call=" << data->typeinfo()->name();
150 }
151 }
152 return true;
153}
154
156{
157 QString s;
158 if (d->mIdDict.find(id) != d->mIdDict.end()) {
159 if (d->mNameMap.contains(id)) {
160 s = i18n("%1 (%2)", d->mNameMap[id], id);
161 } else {
162 s = i18n("Unnamed - ID: %1", id);
163 }
164 } else {
165 // Should _never_ happen
166 s = i18np("%1 unregistered", "%1 unregistered", id);
167 }
168 return s;
169}
170
172{
173 // Prevent direct emitting until all is loaded
175 uint count, i;
176 stream >> count;
177 // qDebug() << ":" << count << "KGameProperty objects";
178 for (i = 0; i < count; ++i) {
179 processMessage(stream, id(), false);
180 }
181 qint16 cookie;
182 stream >> cookie;
183 if (cookie == KPLAYERHANDLER_LOAD_COOKIE) {
184 // qDebug() << " KGamePropertyHandler loaded properly";
185 } else {
186 qCCritical(KDEGAMESPRIVATE_LOG) << "KGamePropertyHandler loading error. probably format error";
187 }
188 // Allow direct emitting (if no other lock still holds)
190 return true;
191}
192
194{
195 // qDebug() << ":" << d->mIdDict.count() << "KGameProperty objects";
196 stream << (uint)d->mIdDict.count();
197 QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
198 while (it.hasNext()) {
199 it.next();
200 KGamePropertyBase *base = it.value();
201 if (base) {
202 KGameMessage::createPropertyHeader(stream, base->id());
203 base->save(stream);
204 }
205 }
206 stream << (qint16)KPLAYERHANDLER_LOAD_COOKIE;
207 return true;
208}
209
211{
212 // qDebug() << ":" << d->mDefaultPolicy;
213 return d->mDefaultPolicy;
214}
216{
217 // qDebug() << ":" << p;
218 d->mDefaultPolicy = p;
219 d->mDefaultUserspace = userspace;
220 QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
221 while (it.hasNext()) {
222 it.next();
223 if (!userspace || it.value()->id() >= KGamePropertyBase::IdUser) {
224 it.value()->setPolicy((KGamePropertyBase::PropertyPolicy)p);
225 }
226 }
227}
228
230{
231 QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
232 while (it.hasNext()) {
233 it.next();
234 it.value()->unlock();
235 }
236}
237
239{
240 QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
241 while (it.hasNext()) {
242 it.next();
243 it.value()->lock();
244 }
245}
246
248{
249 return d->mUniqueId++;
250}
251
253{
254 QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
255 while (it.hasNext()) {
256 it.next();
257 if (it.value()->isDirty()) {
258 it.value()->sendProperty();
259 }
260 }
261}
262
263/* Fire all property signal changed which are collected in
264 * the queque
265 */
267{
268 d->mIndirectEmit++;
269}
270
272{
273 // If the flag is <=0 we emit the queued signals
274 d->mIndirectEmit--;
275 if (d->mIndirectEmit <= 0) {
276 while (!d->mSignalQueue.isEmpty()) {
277 KGamePropertyBase *prop = d->mSignalQueue.dequeue();
278 // qDebug() << "emitting signal for" << prop->id();
280 }
281 }
282}
283
285{
286 // If the indirect flag is set (load and network transmit)
287 // we cannot emit the signals directly as it can happened that
288 // a signal causes an access to a property which is e.g. not
289 // yet loaded or received
290
291 if (d->mIndirectEmit > 0) {
292 // Queque the signal
293 d->mSignalQueue.enqueue(prop);
294 } else {
295 // directly emit
297 }
298}
299
301{
302 bool sent = false;
303 Q_EMIT signalSendMessage(id(), s, &sent);
304 return sent;
305}
306
308{
309 if (d->mIdDict.find(id) == d->mIdDict.end())
310 return nullptr;
311 return *(d->mIdDict.find(id));
312}
313
315{
316 // Note: Hash iterator method 'toFront()' crashes when applied to first item.
317 // Therefore we get the keys as list first.
318 const QList<int> list = d->mIdDict.keys();
319 for (int key : list) {
320 KGamePropertyBase *p = d->mIdDict.value(key);
321 p->unregisterData();
322 if (d->mIdDict.find(p->id()) != d->mIdDict.end()) {
323 // shouldn't happen - but if mOwner in KGamePropertyBase is NULL
324 // this might be possible
326 }
327 }
328}
329
331{
332 return d->mIdDict;
333}
334
336{
337 if (!prop) {
338 return i18n("NULL pointer");
339 }
340
341 QString value;
342
343 const type_info *t = prop->typeinfo();
344 if (*t == typeid(int)) {
345 value = QString::number(((KGamePropertyInt *)prop)->value());
346 } else if (*t == typeid(unsigned int)) {
347 value = QString::number(((KGamePropertyUInt *)prop)->value());
348 } else if (*t == typeid(long int)) {
349 value = QString::number(((KGameProperty<qint64> *)prop)->value());
350 } else if (*t == typeid(unsigned long int)) {
351 value = QString::number(((KGameProperty<quint64> *)prop)->value());
352 } else if (*t == typeid(QString)) {
353 value = ((KGamePropertyQString *)prop)->value();
354 } else if (*t == typeid(qint8)) {
355 value = ((KGamePropertyBool *)prop)->value() ? i18n("True") : i18n("False");
356 } else {
357 Q_EMIT signalRequestValue(prop, value);
358 }
359
360 if (value.isNull()) {
361 value = i18n("Unknown");
362 }
363 return value;
364}
365
367{
368 qDebug() << "-----------------------------------------------------------";
369 qDebug() << "KGamePropertyHandler:: Debug this=" << this;
370
371 qDebug() << " Registered properties: (Policy,Lock,Emit,Optimized, Dirty)";
372 QMultiHashIterator<int, KGamePropertyBase *> it(d->mIdDict);
373 while (it.hasNext()) {
374 it.next();
375 KGamePropertyBase *p = it.value();
376 qDebug() << " " << p->id() << ": p=" << p->policy() << "l=" << p->isLocked() << "e=" << p->isEmittingSignal() << "o=" << p->isOptimized()
377 << "d=" << p->isDirty();
378 }
379 qDebug() << "-----------------------------------------------------------";
380}
381
382#include "moc_kgamepropertyhandler.cpp"
static void extractPropertyHeader(QDataStream &msg, int &id)
Retrieves the property id from a property message header.
static void extractPropertyCommand(QDataStream &msg, int &pid, int &cmd)
Retrieves the property id from a property message header.
static void createPropertyHeader(QDataStream &msg, int id)
Creates a property header given the property id.
Base class of KGameProperty.
bool isOptimized() const
See also setOptimize.
virtual void load(QDataStream &s)=0
This will read the value of this property from the stream.
PropertyPolicy
The policy of the property.
PropertyPolicy policy() const
virtual void save(QDataStream &s)=0
Write the value into a stream.
bool isEmittingSignal() const
See also setEmittingSignal.
virtual const type_info * typeinfo()
bool isDirty() const
virtual void command(QDataStream &stream, int msgid, bool isSender=false)
send a command to advanced properties like arrays
bool isLocked() const
A locked property can only be changed by the player who has set the lock.
A collection class for KGameProperty objects.
bool sendProperty(QDataStream &s)
called by a property to send itself into the datastream.
void setId(int id)
Use id as new ID for this KGamePropertyHandler.
void signalRequestValue(KGamePropertyBase *property, QString &value)
If you call propertyValue with a non-standard KGameProperty it is possible that the value cannot auto...
void setPolicy(KGamePropertyBase::PropertyPolicy p, bool userspace=true)
Set the policy for all kgame variables which are currently registered in the KGame property handler.
void registerHandler(int id, const QObject *receiver, const char *send, const char *emit)
Register the handler with a parent.
void lockProperties()
Calls KGamePropertyBase::setReadOnly(true) for all properties of this handler.
int uniquePropertyId()
returns a unique property ID starting called usually with a base of KGamePropertyBase::IdAutomatic.
QString propertyValue(KGamePropertyBase *property)
In several situations you just want to have a QString of a KGameProperty object.
bool removeProperty(KGamePropertyBase *data)
Removes a property from the handler.
virtual bool save(QDataStream &stream)
Saves properties into the datastream.
QMultiHash< int, KGamePropertyBase * > & dict() const
Reference to the internal dictionary.
void flush()
Sends all properties which are marked dirty over the network.
bool addProperty(KGamePropertyBase *data, const QString &name=QString())
Adds a KGameProperty property to the handler.
void unlockProperties()
Calls KGamePropertyBase::setReadOnly(false) for all properties of this player.
virtual bool load(QDataStream &stream)
Loads properties from the datastream.
void unlockDirectEmit()
Removes the lock from the emitting of property signals.
KGamePropertyBase * find(int id)
void lockDirectEmit()
Called by the KGame or KPlayer object or the handler itself to delay emitting of signals.
void clear()
Clear the KGamePropertyHandler.
KGamePropertyHandler(QObject *parent=nullptr)
Construct an unregistered KGamePropertyHandler.
void emitSignal(KGamePropertyBase *data)
called by a property to emit a signal This call is simply forwarded to the parent object
void signalPropertyChanged(KGamePropertyBase *)
This is emitted by a property.
void Debug()
Writes some debug output to the console.
bool processMessage(QDataStream &stream, int id, bool isSender)
Main message process function.
KGamePropertyBase::PropertyPolicy policy()
Returns the default policy for this property handler.
void signalSendMessage(int msgid, QDataStream &, bool *sent)
This signal is emitted when a property needs to be sent.
QString propertyName(int id) const
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isEmpty() const const
bool isNull() const const
QString number(double n, char format, int precision)
DirectConnection
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:59:04 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.