~kubuntu-members/libkdegames/4.11

« back to all changes in this revision

Viewing changes to libkdegamesprivate/kgame/kgameproperty.h

  • Committer: Stefan Majewsky
  • Date: 2012-05-01 15:34:35 UTC
  • Revision ID: git-v1:82376fb5ca6f29f862641b6ca68603cb76258831
Begin to move stuff into libkdegamesprivate.

The build is now broken because I'm moving stuff without adjusting the
CMake files. But I figured it's cleaner to have the move in one commit
and the various edits in CMake and source files in the next commits.

svn path=/trunk/KDE/kdegames/libkdegames/; revision=1292461

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    This file is part of the KDE games library
 
3
    Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
 
4
    Copyright (C) 2001 Martin Heni (kde at heni-online.de)
 
5
 
 
6
    This library is free software; you can redistribute it and/or
 
7
    modify it under the terms of the GNU Library General Public
 
8
    License version 2 as published by the Free Software Foundation.
 
9
 
 
10
    This library is distributed in the hope that it will be useful,
 
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
    Library General Public License for more details.
 
14
 
 
15
    You should have received a copy of the GNU Library General Public License
 
16
    along with this library; see the file COPYING.LIB.  If not, write to
 
17
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
18
    Boston, MA 02110-1301, USA.
 
19
*/
 
20
 
 
21
#ifndef __KGAMEPROPERTY_H_
 
22
#define __KGAMEPROPERTY_H_
 
23
 
 
24
#include <QtCore/QDataStream>
 
25
 
 
26
#include <kdebug.h>
 
27
#include <typeinfo>
 
28
#include <libkdegames_export.h>
 
29
 
 
30
class KGame;
 
31
class KPlayer;
 
32
class KGamePropertyHandler;
 
33
using namespace std;
 
34
 
 
35
/**
 
36
 * \class KGamePropertyBase kgameproperty.h <KGame/KGameProperty>
 
37
 *
 
38
 * @short Base class of KGameProperty
 
39
 *
 
40
 * The KGamePropertyBase class is the base class of KGameProperty. See
 
41
 * KGameProperty for further information.
 
42
 *
 
43
 * @author Andreas Beckermann <b_mann@gmx.de>
 
44
 **/
 
45
class KDEGAMES_EXPORT KGamePropertyBase
 
46
{
 
47
public:
 
48
        enum PropertyDataIds  { // these belong to KPlayer/KGame!
 
49
                //KPlayer
 
50
                IdGroup=1,
 
51
                IdUserId=2,
 
52
                IdAsyncInput=3,
 
53
                IdTurn=4,
 
54
                IdName=5,
 
55
 
 
56
                //KGame
 
57
                IdGameStatus=6,
 
58
                IdMaxPlayer=7,
 
59
                IdMinPlayer=8,
 
60
 
 
61
    // Input Grabbing
 
62
    IdGrabInput=16,
 
63
    IdReleaseInput=17,
 
64
 
 
65
                IdCommand, // Reserved for internal use
 
66
                IdUser=256,
 
67
 
 
68
                IdAutomatic=0x7000  // Id's from here on are automatically given (16bit)
 
69
        };
 
70
 
 
71
        /**
 
72
         * Commands for advanced properties (qint8)
 
73
         **/
 
74
        enum PropertyCommandIds
 
75
        {
 
76
                // General
 
77
                CmdLock=1,
 
78
 
 
79
                // Array
 
80
                CmdAt=51,
 
81
                CmdResize=52,
 
82
                CmdFill=53,
 
83
                CmdSort=54,
 
84
                // List (could be the same id's actually)
 
85
                CmdInsert=61,
 
86
                CmdAppend=62,
 
87
                CmdRemove=63,
 
88
                CmdClear=64
 
89
        };
 
90
 
 
91
        /**
 
92
         * The policy of the property. This can be PolicyClean (setValue uses
 
93
         * send), PolicyDirty (setValue uses changeValue) or
 
94
         * PolicyLocal (setValue uses setLocal).
 
95
         *
 
96
         * A "clean" policy means that the property is always the same on every
 
97
         * client. This is achieved by calling send which actually changes
 
98
         * the value only when the message from the MessageServer is received.
 
99
         *
 
100
         * A "dirty" policy means that as soon as setValue is called the
 
101
         * property is changed immediately. And additionally sent over network.
 
102
         * This can sometimes lead to bugs as the other clients do not
 
103
         * immediately have the same value. For more information see
 
104
         * changeValue.
 
105
         *
 
106
         * PolicyLocal means that a KGameProperty behaves like ever
 
107
         * "normal" variable. Whenever setValue is called (e.g. using "=")
 
108
         * the value of the property is changes immediately without sending it
 
109
         * over network. You might want to use this if you are sure that all
 
110
         * clients set the property at the same time.
 
111
         **/
 
112
        enum PropertyPolicy
 
113
        {
 
114
                PolicyUndefined = 0,
 
115
                PolicyClean = 1,
 
116
                PolicyDirty = 2,
 
117
                PolicyLocal = 3
 
118
        };
 
119
 
 
120
 
 
121
        /**
 
122
         * Constructs a KGamePropertyBase object and calls registerData.
 
123
         * @param id The id of this property. MUST be UNIQUE! Used to send and
 
124
         * receive changes in the property of the playere automatically via
 
125
         * network.
 
126
         * @param owner The owner of the object. Must be a KGamePropertyHandler which manages
 
127
         * the changes made to this object, i.e. which will send the new data
 
128
         **/
 
129
        KGamePropertyBase(int id, KGamePropertyHandler* owner);
 
130
 
 
131
        KGamePropertyBase(int id, KGame* parent);
 
132
        KGamePropertyBase(int id, KPlayer* parent);
 
133
 
 
134
        /**
 
135
         * Creates a KGamePropertyBase object without an owner. Remember to call
 
136
         * registerData!
 
137
         **/
 
138
        KGamePropertyBase();
 
139
 
 
140
        virtual ~KGamePropertyBase();
 
141
 
 
142
        /**
 
143
         * Changes the consistency policy of a property. The
 
144
         * PropertyPolicy is one of PolicyClean (defaulz), PolicyDirty or PolicyLocal.
 
145
         *
 
146
         * It is up to you to decide how you want to work.
 
147
         **/
 
148
        void setPolicy(PropertyPolicy p) { mFlags.bits.policy = p; }
 
149
 
 
150
        /**
 
151
         * @return The default policy of the property
 
152
         **/
 
153
        PropertyPolicy policy() const { return (PropertyPolicy)mFlags.bits.policy; }
 
154
 
 
155
        /**
 
156
         * Sets this property to emit a signal on value changed.
 
157
         * As the properties do not inherit QObject for optimization
 
158
         * this signal is emitted via the KPlayer or KGame object
 
159
         **/
 
160
        void setEmittingSignal(bool p)  { mFlags.bits.emitsignal=p; }
 
161
 
 
162
        /**
 
163
         * See also setEmittingSignal
 
164
         * @return Whether this property emits a signal on value change
 
165
         **/
 
166
        bool isEmittingSignal() const { return mFlags.bits.emitsignal; }
 
167
 
 
168
        /**
 
169
         * Sets this property to try to optimize signal and network handling
 
170
         * by not sending it out when the property value is not changed.
 
171
         **/
 
172
        void setOptimized(bool p) { mFlags.bits.optimize = p ; }
 
173
 
 
174
        /**
 
175
         * See also setOptimize
 
176
         * @return Whether the property optimizes access (signals,network traffic)
 
177
         **/
 
178
        bool isOptimized() const { return mFlags.bits.optimize; }
 
179
 
 
180
        /**
 
181
         * @return Whether this property is "dirty". See also setDirty
 
182
         **/
 
183
        bool isDirty() const { return mFlags.bits.dirty; }
 
184
 
 
185
        /**
 
186
         * A locked property can only be changed by the player who has set the
 
187
         * lock. See also setLocked
 
188
         * @return Whether this property is currently locked.
 
189
         **/
 
190
        bool isLocked() const { return mFlags.bits.locked; }
 
191
 
 
192
        /**
 
193
         * A locked property can only be changed by the player who has set the
 
194
         * lock.
 
195
         *
 
196
         * You can only call this if isLocked is false. A message is sent
 
197
         * over network so that the property is locked for all players except
 
198
         * you.
 
199
         *
 
200
         * @return returns false if the property can not be locked, i.e. it is already locked
 
201
         *
 
202
         **/
 
203
        bool lock();
 
204
 
 
205
        /**
 
206
         * A locked property can only be changed by the player who has set the
 
207
         * lock.
 
208
         *
 
209
         * You can only call this if isLocked is false. A message is sent
 
210
         * over network so that the property is locked for all players except
 
211
         * you.
 
212
         *
 
213
         * @return returns false if the property can not be locked, i.e. it is already locked
 
214
         *
 
215
         **/
 
216
        bool unlock(bool force=false);
 
217
 
 
218
        /**
 
219
         * This will read the value of this property from the stream. You MUST
 
220
         * overwrite this method in order to use this class
 
221
         * @param s The stream to read from
 
222
         **/
 
223
        virtual void load(QDataStream& s) = 0;
 
224
 
 
225
        /**
 
226
         * Write the value into a stream. MUST be overwritten
 
227
         **/
 
228
        virtual void save(QDataStream& s) = 0;
 
229
 
 
230
        /**
 
231
         * send a command to advanced properties like arrays
 
232
         * @param stream The stream containing the data of the comand
 
233
         * @param msgid The ID of the command - see PropertyCommandIds
 
234
         * @param isSender whether this client is also the sender of the command
 
235
         **/
 
236
        virtual void command(QDataStream &stream, int msgid, bool isSender=false);
 
237
 
 
238
        /**
 
239
         * @return The id of this property
 
240
         **/
 
241
        int id() const { return mId; }
 
242
 
 
243
        /**
 
244
         * @return a type_info of the data this property contains. This is used
 
245
         * e.g. by KGameDebugDialog
 
246
         **/
 
247
        virtual const type_info* typeinfo() { return &typeid(this); }
 
248
 
 
249
        /**
 
250
         * You have to register a KGamePropertyBase before you can use it.
 
251
         *
 
252
         * You MUST call this before you can use KGamePropertyBase!
 
253
         *
 
254
         * @param id the id of this KGamePropertyBase object. The id MUST be
 
255
         * unique, i.e. you cannot have two properties with the same id for one
 
256
         * player, although (currently) nothing prevents you from doing so. But
 
257
         * you will get strange results!
 
258
         *
 
259
         * @param owner The owner of this data. This will send the data
 
260
         * using KPropertyHandler::sendProperty whenever you call send
 
261
         *
 
262
         * @param p If not 0 you can set the policy of the property here
 
263
         *
 
264
         * @param name if not 0 you can assign a name to this property
 
265
         *
 
266
         **/
 
267
        int registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, const QString& name=QString());
 
268
 
 
269
        /**
 
270
         * This is an overloaded member function, provided for convenience.
 
271
         * It differs from the above function only in what argument(s) it accepts.
 
272
         **/
 
273
        int registerData(int id, KGamePropertyHandler* owner, const QString& name=QString());
 
274
 
 
275
        /**
 
276
         * This is an overloaded member function, provided for convenience.
 
277
         * It differs from the above function only in what argument(s) it accepts.
 
278
         **/
 
279
        int registerData(int id, KGame* owner, const QString& name=QString());
 
280
 
 
281
        /**
 
282
         * This is an overloaded member function, provided for convenience.
 
283
         * It differs from the above function only in what argument(s) it accepts.
 
284
         **/
 
285
        int registerData(int id, KPlayer* owner, const QString& name=QString());
 
286
 
 
287
        /**
 
288
         * This is an overloaded member function, provided for convenience.
 
289
         * It differs from the above function only in what argument(s) it accepts.
 
290
         * In particular you can use this function to create properties which
 
291
         * will have an automatic id assigned. The new id is returned.
 
292
         **/
 
293
        int registerData(KGamePropertyHandler* owner,PropertyPolicy p=PolicyUndefined, const QString& name=QString() );
 
294
 
 
295
        void unregisterData();
 
296
 
 
297
 
 
298
protected:
 
299
        /**
 
300
         * A locked property can only be changed by the player who has set the
 
301
         * lock.
 
302
         *
 
303
         * You can only call this if isLocked is false. A message is sent
 
304
         * over network so that the property is locked for all players except
 
305
         * you.
 
306
         * Usually you use lock and unlock to access this property
 
307
         *
 
308
         **/
 
309
        void setLock(bool l);
 
310
 
 
311
        /**
 
312
         * Sets the "dirty" flag of the property. If a property is "dirty" i.e.
 
313
         * KGameProperty::setLocal has been called there is no guarantee
 
314
         * that all clients share the same value. You have to ensure this
 
315
         * yourself e.g. by calling KGameProperty::setLocal on every
 
316
         * client. You can also ignore the dirty flag and continue working withe
 
317
         * the property depending on your situation.
 
318
         **/
 
319
        void setDirty(bool d) { mFlags.bits.dirty = d ; }
 
320
 
 
321
        /**
 
322
         * Forward the data to the owner of this property which then sends it
 
323
         * over network. save is used to store the data into a stream so
 
324
         * you have to make sure that function is working properly if you
 
325
         * implement your own property!
 
326
         *
 
327
         * Note: this sends the <em>current</em> property!
 
328
         *
 
329
         * Might be obsolete - KGamePropertyArray still uses it. Is this a bug
 
330
         * or correct?
 
331
         **/
 
332
        bool sendProperty();
 
333
 
 
334
        /**
 
335
         * Forward the data to the owner of this property which then sends it
 
336
         * over network. save is used to store the data into a stream so
 
337
         * you have to make sure that function is working properly if you
 
338
         * implement your own property!
 
339
         *
 
340
         * This function is used by send to send the data over network.
 
341
         * This does <em>not</em> send the current value but the explicitly
 
342
         * given value.
 
343
         *
 
344
         * @return TRUE if the message could be sent successfully, otherwise
 
345
         * FALSE
 
346
         **/
 
347
        bool sendProperty(const QByteArray& b);
 
348
 
 
349
        /**
 
350
         * Causes the parent object to emit a signal on value change
 
351
         **/
 
352
        void emitSignal();
 
353
 
 
354
protected:
 
355
        KGamePropertyHandler* mOwner;
 
356
 
 
357
        // Having this as a union of the bitfield and the char
 
358
        // allows us to stream this quantity easily (if we need to)
 
359
        // At the moment it is not yet transmitted
 
360
        union Flags {
 
361
                char flag;
 
362
                struct {
 
363
                        // unsigned char dosave : 1;   // do save this property
 
364
                        // unsigned char delaytransmit : 1;   // do not send immediately on
 
365
                                             // change but a KPlayer:QTimer
 
366
                                             // sends it later on - fast
 
367
                                             // changing variables
 
368
                        unsigned char emitsignal : 1; // KPlayer notifies on variable change (true)
 
369
                        //unsigned char readonly : 1; // whether the property can be changed (false)
 
370
                        unsigned char optimize : 1; // whether the property tries to optimize send/emit (false)
 
371
                        unsigned char dirty: 1; // whether the property dirty (setLocal() was used)
 
372
                        unsigned char policy : 2; // whether the property is always consistent (see PropertyPolicy)
 
373
                        unsigned char locked: 1; // whether the property is locked (true)
 
374
                } bits;
 
375
        } mFlags;
 
376
 
 
377
private:
 
378
        friend class KGamePropertyHandler;
 
379
        void init();
 
380
 
 
381
private:
 
382
        int mId;
 
383
 
 
384
};
 
385
 
 
386
/**
 
387
 * \class KGameProperty kgameproperty.h <KGame/KGameProperty>
 
388
 *
 
389
 * @short A class for network transparent games
 
390
 *
 
391
 * Note: The entire API documentation is obsolete!
 
392
 *
 
393
 * The class KGameProperty can store any form of data and will transmit it via
 
394
 * network whenver you call send. This makes network transparent games
 
395
 * very easy. You first have to register the data to a KGamePropertyHandler
 
396
 * using KGamePropertyBase::registerData (which is called by the
 
397
 * constructor). For the KGamePropertyHandler you can use
 
398
 * KGame::dataHandler or KPlayer::dataHandler but you can also create your
 
399
 * own data handler.
 
400
 *
 
401
 * There are several concepts you can follow when writing network games. These
 
402
 * concepts differ completely from the way how data is transferred so you should
 
403
 * decide which one to use. You can also mix these concepts for a single
 
404
 * property but we do not recommend this. The concepts:
 
405
 * <ul>
 
406
 * <li> Always Consistent (clean)
 
407
 * <li> Not Always Consistent (dirty)
 
408
 * <li> A Mixture (very dirty)
 
409
 * </ul>
 
410
 * I repeat: we do <em>not</em> recommend the third option ("a mixture"). Unless
 
411
 * you have a good reason for this you will probably introduce some hard to find
 
412
 * (and to fix) bugs.
 
413
 *
 
414
 * @section Always consistent (clean):
 
415
 *
 
416
 * This "policy" is default. Whenever you create a KGameProperty it is always
 
417
 * consistent. This means that consistency is the most important thing for the
 
418
 * property. This is achieved by using send to change the value of the
 
419
 * property. send needs a running KMessageServer and therefore
 
420
 * <em>MUST</em> be plugged into a KGamePropertyHandler using either
 
421
 * registerData or the constructor. The parent of the dataHandler must be able
 
422
 * to send messages (see above: the message server must be running). If you use
 
423
 * send to change the value of a property you won't see the effect
 
424
 * immediately: The new value is first transferred to the message server which
 
425
 * queues the message. As soon as <em>all</em> messages in the message server
 
426
 * which are before the changed property have been transferred the message
 
427
 * server delivers the new value of the KGameProperty to all clients. A
 
428
 * QTimer::singleShot is used to queue the messages inside the
 
429
 * KMessageServer.
 
430
 *
 
431
 * This means that if you do the following:
 
432
 * \code
 
433
 * KGamePropertyInt myProperty(id, dataHandler());
 
434
 * myProperty.initData(0);
 
435
 * myProperty = 10;
 
436
 * int value = myProperty.value();
 
437
 * \endcode
 
438
 * then "value" will be "0". initData is used to initialize the property
 
439
 * (e.g. when the KMessageServer is not yet running or can not yet be
 
440
 * reached). This is because "myProperty = 10" or "myProperty.send(10)" send a
 
441
 * message to the KMessageServer which uses QTimer::singleShot to
 
442
 * queue the message. The game first has to go back into the event loop where
 
443
 * the message is received. The KGamePropertyHandler receives the new value
 
444
 * sets the property. So if you need the new value you need to store it in a
 
445
 * different variable (see setLocal which creates one for you until the
 
446
 * message is received). The KGamePropertyHandler emits a signal (unless
 
447
 * you called setEmitSignal with false) when the new value is received:
 
448
 * KGamePropertyHandler::signalPropertyChanged. You can use this to react
 
449
 * to a changed property.
 
450
 *
 
451
 * This may look quite confusing but it has a <em>big</em> advantage: all
 
452
 * KGameProperty objects are ensured to have the same value on all clients in
 
453
 * the game at every time. This way you will save you a lot of trouble as
 
454
 * debugging can be very difficult if the value of a property changes
 
455
 * immediately on client A but only after one or two additianal messages
 
456
 * (function calls, status changes, ...) on client B.
 
457
 *
 
458
 * The only disadvantage of this (clean) concept is that you cannot use a
 
459
 * changed variable immediately but have to wait for the KMessageServer to
 
460
 * change it. You probably want to use
 
461
 * KGamePropertyHandler::signalPropertyChanged for this.
 
462
 *
 
463
 * @section Not Always Consistent (dirty):
 
464
 *
 
465
 * There are a lot of people who don't want to use the (sometimes quite complex)
 
466
 * "clean" way. You can use setAlwaysConsistent to change the default
 
467
 * behaviour of the KGameProperty. If a property is not always consistent
 
468
 * it will use changeValue to send the property.  changeValue also uses
 
469
 * send to send the new value over network but it also uses
 
470
 * setLocal to create a local copy of the property. This copy is created
 
471
 * dynamically and is deleted again as soon as the next message from the network
 
472
 * is received. To use the example above again:
 
473
 * \code
 
474
 * KGamePropertyInt myProperty(id, dataHandler());
 
475
 * myProperty.setAlwaysConsistent(false);
 
476
 * myProperty.initData(0);
 
477
 * myProperty = 10;
 
478
 * int value = myProperty.value();
 
479
 * \endcode
 
480
 * Now this example will "work" so value now is 10. Additionally the
 
481
 * KMessageServer receives a message from the local client (just as explained
 
482
 * above in "Always Consistent"). As soon as the message returns to the local
 
483
 * client again the local value is deleted, as the "network value" has the same
 
484
 * value as the local one. So you won't lose the ability to use the always
 
485
 * consistent "clean" value of the property if you use the "dirty" way. Just use
 
486
 * networkValue to access the value which is consistent among all clients.
 
487
 *
 
488
 * The advantage of this concept is clear: you can use a KGameProperty as
 
489
 * every other variable as the changes value takes immediate effect.
 
490
 * Additionally you can be sure that the value is transferred to all clients.
 
491
 * You will usually not experience serious bugs just because you use the "dirty"
 
492
 * way. Several events have to happen at once to get these "strange errors"
 
493
 * which result in inconsistent properties (like "game running" on client A but
 
494
 * "game ended/paused" on client B).  But note that there is a very good reason
 
495
 * for the existence of these different concepts of KGameProperty. I have
 
496
 * myself experienced such a "strange error" and it took me several days to find
 
497
 * the reason until I could fix it. So I personally recommend the "clean" way.
 
498
 * On the other hand if you want to port a non-network game to a network game
 
499
 * you will probably start with "dirty" properties as it is you will not have to
 
500
 * change that much code...
 
501
 *
 
502
 * @section A Mixture (very dirty):
 
503
 *
 
504
 * You can also mix the concepts above. Note that we really don't recommend
 
505
 * this. With a mixture I mean something like this:
 
506
 * \code
 
507
 * KGamePropertyInt myProperty(id, dataHandler());
 
508
 * myProperty.setAlwaysConsistent(false);
 
509
 * myProperty.initData(0);
 
510
 * myProperty = 10;
 
511
 * myProperty.setAlwaysConsistent(true);
 
512
 * myProperty = 20;
 
513
 * \endcode
 
514
 * (totally senseless example, btw) I.e. I am speaking of mixing both concepts
 
515
 * for a single property. Things like
 
516
 * \code
 
517
 * KGamePropertyInt myProperty1(id1, dataHandler());
 
518
 * KGamePropertyInt myProperty2(id2, dataHandler());
 
519
 * myProperty1.initData(0);
 
520
 * myProperty2.initData(0);
 
521
 * myProperty1.setAlwaysConsistent(false);
 
522
 * myProperty2.setAlwaysConsistent(true);
 
523
 * myProperty1 = 10;
 
524
 * myProperty2 = 20;
 
525
 * \endcode
 
526
 * are ok. But mixing the concepts for a single property will make it nearly
 
527
 * impossible to you to debug your game.
 
528
 *
 
529
 * So the right thing to do(tm) is to decide in the constructor whether you want
 
530
 * a "clean" or "dirty" property.
 
531
 *
 
532
 * Even if you have decided for one of the concepts you still can manually
 
533
 * follow another concept than the "policy" of your property. So if you use an
 
534
 * always consistent KGameProperty you still can manually call
 
535
 * changeValue as if it was not always consistent. Note that although this is
 
536
 * also kind of a "mixture" as described above this is very useful sometimes. In
 
537
 * contrast to the "mixture" above you don't have the problem that you don't
 
538
 * exactly know which concept you are currently following because you used the
 
539
 * function of the other concept only once.
 
540
 *
 
541
 * @section Custom classes:
 
542
 *
 
543
 * If you want to use a custum class with KGameProperty you have to implement the
 
544
 * operators << and >> for QDataStream:
 
545
 * \code
 
546
 * class Card
 
547
 * {
 
548
 * public:
 
549
 * int type;
 
550
 * int suite;
 
551
 * };
 
552
 * QDataStream& operator<<(QDataStream& stream, Card& card)
 
553
 * {
 
554
 * qint16 type = card.type;
 
555
 * qint16 suite = card.suite;
 
556
 * s << type;
 
557
 * s << suite;
 
558
 * return s;
 
559
 * }
 
560
 * QDataStream& operator>>(QDataStream& stream, Card& card)
 
561
 * {
 
562
 * qint16 type;
 
563
 * qint16 suite;
 
564
 * s >> type;
 
565
 * s >> suite;
 
566
 * card.type = (int)type;
 
567
 * card.suite = (int)suite;
 
568
 * return s;
 
569
 * }
 
570
 *
 
571
 * class Player : KPlayer
 
572
 * {
 
573
 * [...]
 
574
 * KGameProperty<Card> mCards;
 
575
 * };
 
576
 * \endcode
 
577
 *
 
578
 * Note: unlike most QT classes KGameProperty objects are *not* deleted
 
579
 * automatically! So if you create an object using e.g. KGameProperty<int>* data =
 
580
 * new KGameProperty(id, dataHandler()) you have to put a delete data into your
 
581
 * destructor!
 
582
 *
 
583
 * @author Andreas Beckermann <b_mann@gmx.de>
 
584
 **/
 
585
template<class type>
 
586
class KGameProperty  : public KGamePropertyBase
 
587
{
 
588
public:
 
589
        /**
 
590
         * Constructs a KGameProperty object. A KGameProperty object will transmit
 
591
         * any changes to the KMessageServer and then to all clients in the
 
592
         * game (including the one that has sent the new value)
 
593
         * @param id The id of this property. <em>MUST be UNIQUE</em>! Used to send and
 
594
         * receive changes in the property of the playere automatically via
 
595
         * network.
 
596
         * @param owner The parent of the object. Must be a KGame which manages
 
597
         * the changes made to this object, i.e. which will send the new data.
 
598
         * Note that in contrast to most KDE/QT classes KGameProperty objects
 
599
         * are <em>not</em> deleted automatically!
 
600
         **/
 
601
// TODO: ID: Very ugly - better use something like parent()->propertyId() or so which assigns a free id automatically.
 
602
        KGameProperty(int id, KGamePropertyHandler* owner) : KGamePropertyBase(id, owner) { init(); }
 
603
 
 
604
        /**
 
605
         * This constructor does nothing. You have to call
 
606
         * KGamePropertyBase::registerData
 
607
         * yourself before using the KGameProperty object.
 
608
         **/
 
609
        KGameProperty() : KGamePropertyBase() { init(); }
 
610
 
 
611
        virtual ~KGameProperty() {}
 
612
 
 
613
        /**
 
614
         * Set the value depending on the current policy (see
 
615
         * setConsistent). By default KGameProperty just uses send to set
 
616
         * the value of a property. This behaviour can be changed by using
 
617
         * setConsistent.
 
618
         * @param v The new value of the property
 
619
         **/
 
620
        void setValue(type v)
 
621
        {
 
622
                switch (policy()) {
 
623
                        case PolicyClean:
 
624
                                send(v);
 
625
                                break;
 
626
                        case PolicyDirty:
 
627
                                changeValue(v);
 
628
                                break;
 
629
                        case PolicyLocal:
 
630
                                setLocal(v);
 
631
                                break;
 
632
                        default: // NEVER!
 
633
                                kError(11001) << "Undefined Policy in property" << id();
 
634
                                return;
 
635
                }
 
636
        }
 
637
 
 
638
 
 
639
        /**
 
640
         * This function sends a new value over network.
 
641
         *
 
642
         * Note that the value DOES NOT change when you call this function. This
 
643
         * function saves the value into a QDataStream and calls
 
644
         * sendProperty where it gets forwarded to the owner and finally the
 
645
         * value is sent over network. The KMessageServer now sends the
 
646
         * value to ALL clients - even the one who called this function. As soon
 
647
         * as the value from the message server is received load is called
 
648
         * and _then_ the value of the KGameProperty has been set.
 
649
         *
 
650
         * This ensures that a KGameProperty has _always_ the same value on
 
651
         * _every_ client in the network. Note that this means you can NOT do
 
652
         * something like
 
653
         * \code
 
654
         * myProperty.send(1);
 
655
         * doSomething(myProperty);
 
656
         * \endcode
 
657
         * as myProperty has not yet been set when doSomething is being called.
 
658
         *
 
659
         * You are informed about a value change by a signal from the parent of
 
660
         * the property which can be deactivated by setEmittingSignal because of
 
661
         * performance (you probably don't have to deactivate it - except you
 
662
         * want to write a real-time game like Command&Conquer with a lot of
 
663
         * acitvity). See emitSignal
 
664
         *
 
665
         * Note that if there is no KMessageServer accessible - before
 
666
         * the property has been registered to the KGamePropertyHandler (as
 
667
         * it is the case e.g. before a KPlayer has been plugged into the
 
668
         * KGame object) the property is *not* sent but set *locally* (see
 
669
         * setLocal)!
 
670
         *
 
671
         * @param v The new value of the property
 
672
         * @return whether the property could be sent successfully
 
673
         * @see setValue setLocal changeValue value
 
674
         **/
 
675
        bool send(type v)
 
676
        {
 
677
                if (isOptimized() && mData == v) {
 
678
                        return true;
 
679
                }
 
680
                if (isLocked()) {
 
681
                        return false;
 
682
                }
 
683
                QByteArray b;
 
684
                QDataStream stream(&b, QIODevice::WriteOnly);
 
685
                stream << v;
 
686
                if (!sendProperty(b)) {
 
687
                        setLocal(v);
 
688
                        return false;
 
689
                }
 
690
                return true;
 
691
        }
 
692
 
 
693
        /**
 
694
         * This function sets the value of the property directly, i.e. it
 
695
         * doesn't send it to the network.
 
696
         *
 
697
         * Int contrast to @see you change _only_ the local value when using
 
698
         * this function. You do _not_ change the value of any other client. You
 
699
         * probably don't want to use this if you are using a dedicated server
 
700
         * (which is the only "client" which is allowed to change a value) but
 
701
         * rather want to use send().
 
702
         *
 
703
         * But if you use your clients as servers (i.e. all clients receive a
 
704
         * players turn and then calculate the reaction of the game theirselves)
 
705
         * then you probably want to use setLocal as you can do things like
 
706
         * \code
 
707
         * myProperty.setLocal(1);
 
708
         * doSomething(myProperty);
 
709
         * \endcode
 
710
         * on every client.
 
711
         *
 
712
         * If you want to set the value locally AND send it over network you
 
713
         * want to call changeValue!
 
714
         *
 
715
         * You can also use setPolicy to set the default policy to
 
716
         * PolicyLocal.
 
717
         *
 
718
         * @see setValue send changeValue value
 
719
         **/
 
720
        bool setLocal(type v)
 
721
        {
 
722
                if (isOptimized() && mData == v) {
 
723
                        return false;
 
724
                }
 
725
                if (isLocked()) {
 
726
                        return false;
 
727
                }
 
728
                mData = v;
 
729
                setDirty(true);
 
730
                if (isEmittingSignal()) {
 
731
                        emitSignal();
 
732
                }
 
733
                return true;
 
734
        }
 
735
 
 
736
        /**
 
737
         * This function does both, change the local value and change the
 
738
         * network value. The value is sent over network first, then changed
 
739
         * locally.
 
740
         *
 
741
         * This function is a convenience function and just calls send
 
742
         * followed by setLocal
 
743
         *
 
744
         * Note that emitSignal is also called twice: once after
 
745
         * setLocal and once when the value from send is received
 
746
         *
 
747
         * @see send setLocal setValue value
 
748
         **/
 
749
        void changeValue(type v)
 
750
        {
 
751
                send(v);
 
752
                setLocal(v);
 
753
        }
 
754
 
 
755
        /**
 
756
         * Saves the object to a stream.
 
757
         * @param stream The stream to save to
 
758
         **/
 
759
        virtual void save(QDataStream &stream)
 
760
        {
 
761
                stream << mData;
 
762
        }
 
763
 
 
764
        /**
 
765
         * @return The local value (see setLocal) if it is existing,
 
766
         * otherwise the network value which is always consistent on every
 
767
         * client.
 
768
         **/
 
769
        const type& value() const
 
770
        {
 
771
                return mData;
 
772
        }
 
773
 
 
774
        /**
 
775
         * Reads from a stream and assigns the read value to this object.
 
776
         *
 
777
         * This function is called automatically when a new value is received
 
778
         * over network (i.e. it has been sent using send on this or any
 
779
         * other client) or when a game is loaded (and maybe on some other
 
780
         * events).
 
781
         *
 
782
         * Also calls emitSignal if isEmittingSignal is TRUE.
 
783
         * @param s The stream to read from
 
784
         **/
 
785
        virtual void load(QDataStream& s)
 
786
        {
 
787
                s >> mData;
 
788
                setDirty(false);
 
789
                if (isEmittingSignal()) {
 
790
                        emitSignal();
 
791
                }
 
792
        }
 
793
 
 
794
        /**
 
795
         * This calls setValue to change the value of the property. Note
 
796
         * that depending on the policy (see setAlwaysConsistent) the
 
797
         * returned value might be different from the assigned value!!
 
798
         *
 
799
         * So if you use setPolicy(PolicyClean):
 
800
         * \code
 
801
         * int a, b = 10;
 
802
         * myProperty = b;
 
803
         * a = myProperty.value();
 
804
         * \endcode
 
805
         * Here a and b would differ!
 
806
         * The value is actually set as soon as it is received from the
 
807
         * KMessageServer which forwards it to ALL clients in the network.
 
808
         *
 
809
         * If you use a clean policy (see setPolicy) then
 
810
         * the returned value is the assigned value
 
811
         **/
 
812
        const type& operator=(const type& t)
 
813
        {
 
814
                setValue(t);
 
815
                return value();
 
816
        }
 
817
 
 
818
        /**
 
819
         * This copies the data of property to the KGameProperty object.
 
820
         *
 
821
         * Equivalent to setValue(property.value());
 
822
         **/
 
823
        const type& operator=(const KGameProperty& property)
 
824
        {
 
825
                setValue(property.value());
 
826
                return value();
 
827
        }
 
828
 
 
829
        /**
 
830
         * Yeah, you can do it!
 
831
         * \code
 
832
         *      int a = myGamePropertyInt;
 
833
         * \endcode
 
834
         * If you don't see it: you don't have to use integerData.value()
 
835
         **/
 
836
        operator type() const { return value(); }
 
837
 
 
838
        virtual const type_info* typeinfo() { return &typeid(type); }
 
839
 
 
840
private:
 
841
        void init() { }
 
842
 
 
843
private:
 
844
        type mData;
 
845
};
 
846
 
 
847
 
 
848
typedef KGameProperty<int>   KGamePropertyInt;
 
849
typedef KGameProperty<unsigned int>   KGamePropertyUInt;
 
850
typedef KGameProperty<QString>   KGamePropertyQString;
 
851
typedef KGameProperty<qint8>   KGamePropertyBool;
 
852
 
 
853
#endif