1 /*
2 This file is part of the KDE games library
3 SPDX-FileCopyrightText: 2001 Martin Heni <kde at heni-online.de>
4 SPDX-FileCopyrightText: 2001 Andreas Beckermann <b_mann@gmx.de>
5
6 SPDX-License-Identifier: LGPL-2.0-only
7 */
8
9 #include "kplayer.h"
10
11 // own
12 #include "kgame.h"
13 #include "kgameio.h"
14 #include "kgamemessage.h"
15 #include "kgameproperty.h"
16 #include "kgamepropertyhandler.h"
17 // KF
18 #include <KLocalizedString>
19 // Qt
20 #include <QBuffer>
21 // Std
22 #include <cstdio>
23 #include <cassert>
24
25 #define KPLAYER_LOAD_COOKIE 7285
26
27 class KPlayerPrivate
28 {
29 public:
KPlayerPrivate()30 KPlayerPrivate()
31 {
32 mNetworkPlayer = nullptr;
33 }
34
35 KGame *mGame;
36 bool mActive; // active player
37 KPlayer::KGameIOList mInputList;
38
39 // GameProperty
40 KGamePropertyBool mAsyncInput; // async input allowed
41 KGamePropertyBool mMyTurn; // Is it my turn to play (only useful if not async)?
42 KGamePropertyInt mUserId; // a user defined id
43
44 quint32 mId;
45 bool mVirtual; // virtual player
46 int mPriority; // tag for replacement
47
48 KPlayer* mNetworkPlayer; // replacement player
49
50 KGamePropertyHandler mProperties;
51
52 // Playerdata
53 KGamePropertyQString mName;
54 KGamePropertyQString mGroup;
55 };
56
KPlayer()57 KPlayer::KPlayer()
58 : QObject(), d(new KPlayerPrivate)
59 {
60 init();
61 }
62
init()63 void KPlayer::init()
64 {
65 // note that NO KGame object exists here! so we cannot use KGameProperty::send!
66 qCDebug(GAMES_PRIVATE_KGAME) << ": this=" << this << ", sizeof(this)="<<sizeof(KPlayer);
67 qCDebug(GAMES_PRIVATE_KGAME) << "sizeof(m_Group)="<<sizeof(d->mGroup);
68
69 d->mProperties.registerHandler(KGameMessage::IdPlayerProperty,
70 this,SLOT(sendProperty(int,QDataStream&,bool*)),
71 SLOT(emitSignal(KGamePropertyBase*)));
72 d->mVirtual=false;
73 d->mActive=true;
74 d->mGame=nullptr;
75 d->mId=0; // "0" is always an invalid ID!
76 d->mPriority=0;
77 // I guess we cannot translate the group otherwise no
78 // international connections are possible
79
80 d->mUserId.registerData(KGamePropertyBase::IdUserId, this, i18n("UserId"));
81 d->mUserId.setLocal(0);
82 d->mGroup.registerData(KGamePropertyBase::IdGroup, this, i18n("Group"));
83 d->mGroup.setLocal(i18n("default"));
84 d->mName.registerData(KGamePropertyBase::IdName, this, i18n("Name"));
85 d->mName.setLocal(i18n("default"));
86
87 d->mAsyncInput.registerData(KGamePropertyBase::IdAsyncInput, this, i18n("AsyncInput"));
88 d->mAsyncInput.setLocal(false);
89 d->mMyTurn.registerData(KGamePropertyBase::IdTurn, this, i18n("myTurn"));
90 d->mMyTurn.setLocal(false);
91 d->mMyTurn.setEmittingSignal(true);
92 d->mMyTurn.setOptimized(false);
93 }
94
~KPlayer()95 KPlayer::~KPlayer()
96 {
97 qCDebug(GAMES_PRIVATE_KGAME) << ": this=" << this <<", id=" << this->id();
98
99 // Delete IODevices
100 qDeleteAll(d->mInputList);
101 d->mInputList.clear();
102 if (game())
103 {
104 game()->playerDeleted(this);
105 }
106
107 // note: mProperties does not use autoDelete or so - user must delete objects
108 // himself
109 d->mProperties.clear();
110 qCDebug(GAMES_PRIVATE_KGAME) << "done";
111 }
112
rtti() const113 int KPlayer::rtti() const
114 {
115 return 0;
116 }
117
ioList()118 KPlayer::KGameIOList* KPlayer::ioList()
119 {
120 return &d->mInputList;
121 }
122
setGame(KGame * game)123 void KPlayer::setGame(KGame *game)
124 {
125 d->mGame = game;
126 }
127
game() const128 KGame* KPlayer::game() const
129 {
130 return d->mGame;
131 }
132
setAsyncInput(bool a)133 void KPlayer::setAsyncInput(bool a)
134 {
135 d->mAsyncInput = a;
136 }
137
asyncInput() const138 bool KPlayer::asyncInput() const
139 {
140 return d->mAsyncInput.value();
141 }
142
isActive() const143 bool KPlayer::isActive() const
144 {
145 return d->mActive;
146 }
147
setActive(bool v)148 void KPlayer::setActive(bool v)
149 {
150 d->mActive = v;
151 }
152
userId() const153 int KPlayer::userId() const
154 {
155 return d->mUserId.value();
156 }
157
setUserId(int i)158 void KPlayer::setUserId(int i)
159 {
160 d->mUserId = i;
161 }
162
myTurn() const163 bool KPlayer::myTurn() const
164 {
165 return d->mMyTurn.value();
166 }
167
forwardMessage(QDataStream & msg,int msgid,quint32 receiver,quint32 sender)168 bool KPlayer::forwardMessage(QDataStream &msg,int msgid,quint32 receiver,quint32 sender)
169 {
170 if (!isActive())
171 {
172 return false;
173 }
174 if (!game())
175 {
176 return false;
177 }
178 qCDebug(GAMES_PRIVATE_KGAME) << ": to game sender="<<sender<<"" << "recv="<<receiver <<"msgid="<<msgid;
179 return game()->sendSystemMessage(msg,msgid,receiver,sender);
180 }
181
forwardInput(QDataStream & msg,bool transmit,quint32 sender)182 bool KPlayer::forwardInput(QDataStream &msg,bool transmit,quint32 sender)
183 {
184 if (!isActive())
185 {
186 return false;
187 }
188 if (!game())
189 {
190 return false;
191 }
192
193 qCDebug(GAMES_PRIVATE_KGAME) << ": to game playerInput(sender="<<sender<<")";
194 if (!asyncInput() && !myTurn())
195 {
196 qCDebug(GAMES_PRIVATE_KGAME) << ": rejected cause it is not our turn";
197 return false;
198 }
199
200 // AB: I hope I remember the usage correctly:
201 // this function is called twice (on sender side) - once with transmit = true
202 // where it sends the input to the comserver and once with transmit = false
203 // where it really looks at the input
204 if (transmit)
205 {
206 qCDebug(GAMES_PRIVATE_KGAME) << "indirect playerInput";
207 return game()->sendPlayerInput(msg,this,sender);
208 }
209 else
210 {
211 qCDebug(GAMES_PRIVATE_KGAME) << "direct playerInput";
212 return game()->systemPlayerInput(msg,this,sender);
213 }
214 }
215
setId(quint32 newid)216 void KPlayer::setId(quint32 newid)
217 {
218 // Needs to be after the sendProcess
219 d->mId=newid;
220 }
221
222
setGroup(const QString & group)223 void KPlayer::setGroup(const QString& group)
224 { d->mGroup = group; }
225
group() const226 const QString& KPlayer::group() const
227 { return d->mGroup.value(); }
228
setName(const QString & name)229 void KPlayer::setName(const QString& name)
230 { d->mName = name; }
231
name() const232 const QString& KPlayer::name() const
233 { return d->mName.value(); }
234
id() const235 quint32 KPlayer::id() const
236 { return d->mId; }
237
dataHandler()238 KGamePropertyHandler * KPlayer::dataHandler()
239 { return &d->mProperties; }
240
setVirtual(bool v)241 void KPlayer::setVirtual(bool v)
242 { d->mVirtual = v; }
243
isVirtual() const244 bool KPlayer::isVirtual() const
245 { return d->mVirtual;}
246
setNetworkPlayer(KPlayer * p)247 void KPlayer::setNetworkPlayer(KPlayer* p)
248 { d->mNetworkPlayer = p; }
249
networkPlayer() const250 KPlayer* KPlayer::networkPlayer() const
251 { return d->mNetworkPlayer; }
252
networkPriority() const253 int KPlayer::networkPriority() const
254 { return d->mPriority; }
255
setNetworkPriority(int p)256 void KPlayer::setNetworkPriority(int p)
257 { d->mPriority = p; }
258
addGameIO(KGameIO * input)259 bool KPlayer::addGameIO(KGameIO *input)
260 {
261 if (!input)
262 {
263 return false;
264 }
265 d->mInputList.append(input);
266 input->initIO(this); // set player and init device
267 return true;
268 }
269
270 // input=0, remove all
removeGameIO(KGameIO * targetinput,bool deleteit)271 bool KPlayer::removeGameIO(KGameIO *targetinput,bool deleteit)
272 {
273 qCDebug(GAMES_PRIVATE_KGAME) << ":" << targetinput << "delete=" << deleteit;
274 bool result=true;
275 if (!targetinput) // delete all
276 {
277 KGameIO *input;
278 while(!d->mInputList.isEmpty())
279 {
280 input = d->mInputList.first();
281 if (input) removeGameIO(input,deleteit);
282 }
283 }
284 else
285 {
286 // qCDebug(GAMES_PRIVATE_KGAME) << "remove IO" << targetinput;
287 if (deleteit)
288 {
289 delete targetinput;
290 }
291 else
292 {
293 targetinput->setPlayer(nullptr);
294 result = d->mInputList.removeAll(targetinput);
295 }
296 }
297 return result;
298 }
299
hasRtti(int rtti) const300 bool KPlayer::hasRtti(int rtti) const
301 {
302 return findRttiIO(rtti) != nullptr;
303 }
304
findRttiIO(int rtti) const305 KGameIO * KPlayer::findRttiIO(int rtti) const
306 {
307 QListIterator<KGameIO*> it(d->mInputList);
308 while (it.hasNext())
309 {
310 KGameIO* curGameIO = it.next();
311 if (curGameIO->rtti() == rtti)
312 {
313 return curGameIO;
314 }
315 }
316 return nullptr;
317 }
318
calcIOValue()319 int KPlayer::calcIOValue()
320 {
321 int value=0;
322 QListIterator<KGameIO*> it(d->mInputList);
323 while (it.hasNext())
324 {
325 value|=it.next()->rtti();
326 }
327 return value;
328 }
329
setTurn(bool b,bool exclusive)330 bool KPlayer::setTurn(bool b, bool exclusive)
331 {
332 qCDebug(GAMES_PRIVATE_KGAME) << ":" << id() << " (" << this << ") to" << b;
333 if (!isActive())
334 {
335 return false;
336 }
337
338 // if we get to do an exclusive turn all other players are disallowed
339 if (exclusive && b && game())
340 {
341 for ( KGame::KGamePlayerList::iterator it = game()->playerList()->begin();
342 it!=game()->playerList()->end(); ++it)
343 {
344 if ((*it)==this)
345 {
346 continue;
347 }
348 (*it)->setTurn(false,false);
349 }
350 }
351
352 // Return if nothing changed
353 d->mMyTurn = b;
354
355 return true;
356 }
357
load(QDataStream & stream)358 bool KPlayer::load(QDataStream &stream)
359 {
360 qint32 id,priority;
361 stream >> id >> priority;
362 setId(id);
363 setNetworkPriority(priority);
364
365 // Load Player Data
366 //FIXME: maybe set all properties setEmitSignal(false) before?
367 d->mProperties.load(stream);
368
369 qint16 cookie;
370 stream >> cookie;
371 if (cookie==KPLAYER_LOAD_COOKIE)
372 {
373 qCDebug(GAMES_PRIVATE_KGAME) << " Player loaded properly";
374 }
375 else
376 {
377 qCCritical(GAMES_PRIVATE_KGAME) << " Player loading error. probably format error";
378 }
379
380 // Q_EMIT signalLoad(stream);
381 return true;
382 }
383
save(QDataStream & stream)384 bool KPlayer::save(QDataStream &stream)
385 {
386 stream << (qint32)id() << (qint32)networkPriority();
387
388 d->mProperties.save(stream);
389
390 stream << (qint16)KPLAYER_LOAD_COOKIE;
391
392 //Q_EMIT signalSave(stream);
393 return true;
394 }
395
396
networkTransmission(QDataStream & stream,int msgid,quint32 sender)397 void KPlayer::networkTransmission(QDataStream &stream,int msgid,quint32 sender)
398 {
399 //qCDebug(GAMES_PRIVATE_KGAME) ": msgid=" << msgid << "sender=" << sender << "we are=" << id();
400 // PlayerProperties processed
401 bool issender;
402 if (game())
403 {
404 issender=sender==game()->gameId();
405 }
406 else
407 {
408 issender=true;
409 }
410 if (d->mProperties.processMessage(stream,msgid,issender))
411 {
412 return ;
413 }
414 switch(msgid)
415 {
416 case KGameMessage::IdPlayerInput:
417 {
418 qCDebug(GAMES_PRIVATE_KGAME) << ": Got player move "
419 << "KPlayer (virtual) forwards it to the game object";
420 forwardInput(stream,false);
421 }
422 break;
423 default:
424 Q_EMIT signalNetworkData(msgid - KGameMessage::IdUser,
425 ((QBuffer*)stream.device())->readAll(),sender,this);
426 qCDebug(GAMES_PRIVATE_KGAME) << ": "
427 << "User data msgid" << msgid;
428 break;
429 }
430
431 }
432
findProperty(int id) const433 KGamePropertyBase* KPlayer::findProperty(int id) const
434 {
435 return d->mProperties.find(id);
436 }
437
addProperty(KGamePropertyBase * data)438 bool KPlayer::addProperty(KGamePropertyBase* data)
439 {
440 return d->mProperties.addProperty(data);
441 }
442
sendProperty(int msgid,QDataStream & stream,bool * sent)443 void KPlayer::sendProperty(int msgid, QDataStream& stream, bool* sent)
444 {
445 if (game())
446 {
447 bool s = game()->sendPlayerProperty(msgid, stream, id());
448 if (s)
449 {
450 *sent = true;
451 }
452 }
453 }
454
emitSignal(KGamePropertyBase * me)455 void KPlayer::emitSignal(KGamePropertyBase *me)
456 {
457 // Notify KGameIO (Process) for a new turn
458 if (me->id()==KGamePropertyBase::IdTurn)
459 {
460 //qCDebug(GAMES_PRIVATE_KGAME) << ": for KGamePropertyBase::IdTurn";
461 QListIterator<KGameIO*> it(d->mInputList);
462 while (it.hasNext())
463 {
464 it.next()->notifyTurn(d->mMyTurn.value());
465 }
466 }
467 Q_EMIT signalPropertyChanged(me,this);
468 }
469
470 // --------------------- DEBUG --------------------
Debug()471 void KPlayer::Debug()
472 {
473 qCDebug(GAMES_PRIVATE_KGAME) << "------------------- KPLAYER -----------------------";
474 qCDebug(GAMES_PRIVATE_KGAME) << "this: " << this;
475 qCDebug(GAMES_PRIVATE_KGAME) << "rtti: " << rtti();
476 qCDebug(GAMES_PRIVATE_KGAME) << "id : " << id();
477 qCDebug(GAMES_PRIVATE_KGAME) << "Name : " << name();
478 qCDebug(GAMES_PRIVATE_KGAME) << "Group: " << group();
479 qCDebug(GAMES_PRIVATE_KGAME) << "Async: " << asyncInput();
480 qCDebug(GAMES_PRIVATE_KGAME) << "myTurn: " << myTurn();
481 qCDebug(GAMES_PRIVATE_KGAME) << "Virtual:" << isVirtual();
482 qCDebug(GAMES_PRIVATE_KGAME) << "Active: " << isActive();
483 qCDebug(GAMES_PRIVATE_KGAME) << "Priority:" << networkPriority();
484 qCDebug(GAMES_PRIVATE_KGAME) << "Game :" << game();
485 qCDebug(GAMES_PRIVATE_KGAME) << "#IOs: " << d->mInputList.count();
486 qCDebug(GAMES_PRIVATE_KGAME) << "---------------------------------------------------";
487 }
488
489
490