KDEGames

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

KDE's Doxygen guidelines are available online.