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