KDEGames

kgame.cpp
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 <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "kgame.h"
10 
11 // own
12 #include "kgamepropertyhandler.h"
13 #include "kgameproperty.h"
14 #include "kplayer.h"
15 #include "kgameio.h"
16 #include "kgameerror.h"
17 #include "kgamesequence.h"
18 #include "kgamemessage.h"
19 // KF
20 #include <KLocalizedString>
21 #include <KRandomSequence>
22 // Qt
23 #include <QBuffer>
24 #include <QTimer>
25 #include <QFile>
26 #include <QQueue>
27 // Std
28 #include <cstdio>
29 #include <cassert>
30 
31 #define KGAME_LOAD_COOKIE 4210
32 
33 Q_LOGGING_CATEGORY(GAMES_PRIVATE_KGAME, "org.kde.games.private.kgame", QtWarningMsg)
34 
35 // try to place as much as possible here
36 // many things are *not* possible here as KGame has to use some inline function
37 class KGamePrivate
38 {
39 public:
40  KGamePrivate()
41  {
42  mUniquePlayerNumber = 0;
43  mPolicy=KGame::PolicyLocal;
44  mGameSequence = nullptr;
45  }
46 
47  int mUniquePlayerNumber;
48  QQueue<KPlayer*> mAddPlayerList;// this is a list of to-be-added players. See addPlayer() docu
49 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(7, 3)
50  KRandomSequence* mRandom;
51 #endif
52  KGame::GamePolicy mPolicy;
53  KGameSequence* mGameSequence;
54 
55 
56  KGamePropertyHandler* mProperties;
57 
58  // player lists
59  KGame::KGamePlayerList mPlayerList;
60  KGame::KGamePlayerList mInactivePlayerList;
61 
62  //KGamePropertys
63  KGamePropertyInt mMaxPlayer;
64  KGamePropertyUInt mMinPlayer;
65  KGamePropertyInt mGameStatus; // Game running?
66  QList<int> mInactiveIdList;
67 
68 };
69 
70 // ------------------- GAME CLASS --------------------------
71 KGame::KGame(int cookie,QObject* parent)
72  : KGameNetwork(cookie,parent),
73  d( new KGamePrivate )
74 {
75  qCDebug(GAMES_PRIVATE_KGAME) << " - " << this << ", sizeof(KGame)=" << sizeof(KGame);
76 
77  d->mProperties = new KGamePropertyHandler(this);
78 
79  d->mProperties->registerHandler(KGameMessage::IdGameProperty,
80  this,SLOT(sendProperty(int,QDataStream&,bool*)),
82  d->mMaxPlayer.registerData(KGamePropertyBase::IdMaxPlayer, this, i18n("MaxPlayers"));
83  d->mMaxPlayer.setLocal(-1); // Infinite
84  d->mMinPlayer.registerData(KGamePropertyBase::IdMinPlayer, this, i18n("MinPlayers"));
85  d->mMinPlayer.setLocal(0); // Always ok
86  d->mGameStatus.registerData(KGamePropertyBase::IdGameStatus, this, i18n("GameStatus"));
87  d->mGameStatus.setLocal(Init);
88  // d->mUniquePlayerNumber = 0;
89 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(7, 3)
90 QT_WARNING_PUSH
91 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
92 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
93  d->mRandom = new KRandomSequence;
94 QT_WARNING_POP
95  d->mRandom->setSeed(0);
96 #endif
97 
101 
103 
104  // BL: FIXME This signal does no longer exist. When we are merging
105  // MH: super....and how do I find out about the lost connection now?
106  // KGame and KGameNetwork, this could be improved!
107 // connect(this,SIGNAL(signalConnectionLost(KGameClient*)),
108 // this,SLOT(slotConnectionLost(KGameClient*)));
109 }
110 
112 {
113  qCDebug(GAMES_PRIVATE_KGAME) ;
114 // Debug();
115  reset();
116  delete d->mGameSequence;
117 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(7, 3)
118  delete d->mRandom;
119 #endif
120  delete d;
121  qCDebug(GAMES_PRIVATE_KGAME) << "done";
122 }
123 
125 {
126  deletePlayers();
127  deleteInactivePlayers();
128  return true;
129 }
130 
131 void KGame::deletePlayers()
132 {
133 // qCDebug(GAMES_PRIVATE_KGAME) ;
134  /* Bugs 303142 and 305000. KPlayer destructor removes
135  * player from the list and makes iterators invalid.
136  * qDeleteAll crashes in that case. */
137  while (!d->mPlayerList.isEmpty())
138  {
139  delete d->mPlayerList.takeFirst();
140  }
141  // qDeleteAll(d->mPlayerList);
142  //NOTE by majewsky: An earlier implementation copied the mPlayerList before
143  //deleting the elements with a takeFirst loop. I therefore chose not to clear()
144  //the list in order not to break anything. The old code had the following
145  //comment: "in case of PolicyClean player=d->mPlayerList.first() is infinite"
146 // qCDebug(GAMES_PRIVATE_KGAME) << "done";
147 }
148 
149 void KGame::deleteInactivePlayers()
150 {
151  qDeleteAll(d->mInactivePlayerList);
152  d->mInactivePlayerList.clear();
153 }
154 
155 bool KGame::load(const QString& filename,bool reset)
156 {
157  if (filename.isNull())
158  {
159  return false;
160  }
161  QFile f(filename);
162  if (!f.open(QIODevice::ReadOnly))
163  {
164  return false;
165  }
166  QDataStream s( &f );
167  load(s,reset);
168  f.close();
169  return true;
170 }
171 
172 bool KGame::load(QDataStream &stream,bool reset)
173 { return loadgame(stream, false,reset); }
174 
175 bool KGame::loadgame(QDataStream &stream, bool network,bool resetgame)
176 {
177  // Load Game Data
178 
179  // internal data
180  qint32 c;
181  stream >> c; // cookie
182 
183  if (c!=cookie())
184  {
185  qCWarning(GAMES_PRIVATE_KGAME) << "Trying to load different game version we="<<cookie() << "saved=" << c;
186  bool result=false;
187  Q_EMIT signalLoadError(stream,network,(int)c,result);
188  return result;
189  }
190  if (resetgame) reset();
191 
192  uint i;
193  stream >> i;
194 // setPolicy((GamePolicy)i);
195 
196  stream >> d->mUniquePlayerNumber;
197 
198  if (gameSequence())
199  {
200  gameSequence()->setCurrentPlayer(nullptr); // TODO !!!
201  }
202 
203 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(7, 3)
204  int newseed;
205  stream >> newseed;
206  d->mRandom->setSeed(newseed);
207 #endif
208 
209  // Switch off the direct emitting of signals while
210  // loading properties. This can cause inconsistencies
211  // otherwise if a property emits and this emit accesses
212  // a property not yet loaded
213  // Note we have to have this external locking to prevent the games unlocking
214  // to access the players
216 
217  for ( KGamePlayerList::iterator it = playerList()->begin(); it!=playerList()->end();++it )
218  {
219  (*it)->dataHandler()->lockDirectEmit();
220  // qCDebug(GAMES_PRIVATE_KGAME) << "Player "<<player->id() << "to indirect emit";
221  }
222 
223  // Properties
224  dataHandler()->load(stream);
225 
226  // If there is additional data to be loaded before players are loaded then do
227  // this here.
229 
230  // Switch back on the direct emitting of signals and emit the
231  // queued signals for properties.
232  // Unlocks properties before loading players in order to make game
233  // initializations related to properties before using them in players
234  // initialization
236 
237  // Load Playerobjects
238  uint playercount;
239  stream >> playercount;
240  qCDebug(GAMES_PRIVATE_KGAME) << "Loading KGame" << playercount << "KPlayer objects";
241  for (i=0;i<playercount;++i)
242  {
243  KPlayer *newplayer=loadPlayer(stream,network);
244  systemAddPlayer(newplayer);
245  }
246 
247  qint16 cookie;
248  stream >> cookie;
249  if (cookie==KGAME_LOAD_COOKIE) {
250  qCDebug(GAMES_PRIVATE_KGAME) << " Game loaded properly";
251  } else {
252  qCCritical(GAMES_PRIVATE_KGAME) << " Game loading error. probably format error";
253  }
254 
255  // Switch back on the direct emitting of signals and emit the
256  // queued signals for players.
257  // Note we habe to have this external locking to prevent the games unlocking
258  // to access the players
259  for ( KGamePlayerList::iterator it = playerList()->begin(); it!=playerList()->end();++it )
260  {
261  (*it)->dataHandler()->unlockDirectEmit();
262  // qCDebug(GAMES_PRIVATE_KGAME) << "Player "<<player->id() << "to direct emit";
263  }
264 
265  Q_EMIT signalLoad(stream);
266  return true;
267 }
268 
269 bool KGame::save(const QString& filename,bool saveplayers)
270 {
271  if (filename.isNull())
272  {
273  return false;
274  }
275  QFile f(filename);
276  if (!f.open(QIODevice::WriteOnly))
277  {
278  return false;
279  }
280  QDataStream s( &f );
281  save(s,saveplayers);
282  f.close();
283  return true;
284 }
285 
286 bool KGame::save(QDataStream &stream,bool saveplayers)
287 { return savegame(stream, false,saveplayers); }
288 
289 bool KGame::savegame(QDataStream &stream,bool /*network*/,bool saveplayers)
290 {
291  // Save Game Data
292 
293  // internal variables
294  qint32 c=cookie();
295  stream << c;
296 
297  uint p=(uint)policy();
298  stream << p;
299  stream << d->mUniquePlayerNumber;
300 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(7, 3)
301  int newseed=(int)d->mRandom->getLong(65535);
302  stream << newseed;
303  d->mRandom->setSeed(newseed);
304 #endif
305 
306  // Properties
307  dataHandler()->save(stream);
308 
309  // Save all data that need to be saved *before* the players are saved
311 
312  if (saveplayers)
313  {
314  savePlayers(stream,playerList());
315  }
316  else
317  {
318  stream << (uint)0; // no players saved
319  }
320 
321  stream << (qint16)KGAME_LOAD_COOKIE;
322 
323  Q_EMIT signalSave(stream);
324  return true;
325 }
326 
328 {
329 // this could be in KGameMessage as well
330  stream << (qint32)p->rtti();
331  stream << (qint32)p->id();
332  stream << (qint32)p->calcIOValue();
333  p->save(stream);
334 }
335 
337 {
338  if (!list)
339  {
340  list=playerList();
341  }
342 
343  qint32 cnt=list->count();
344  qCDebug(GAMES_PRIVATE_KGAME) << "Saving KGame" << cnt << "KPlayer objects";
345  stream << cnt;
346 
347  for ( KGamePlayerList::iterator it = playerList()->begin(); it!=playerList()->end();++it )
348  {
349  savePlayer(stream,*it);
350  }
351 }
352 
353 KPlayer *KGame::createPlayer(int /*rtti*/,int /*io*/,bool /*isvirtual*/)
354 {
355  qCWarning(GAMES_PRIVATE_KGAME) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! ";
356  return new KPlayer;
357 }
358 KPlayer *KGame::loadPlayer(QDataStream& stream,bool isvirtual)
359 {
360  qint32 rtti,id,iovalue;
361  stream >> rtti >> id >> iovalue;
362  KPlayer *newplayer=findPlayer(id);
363  if (!newplayer)
364  {
365  qCDebug(GAMES_PRIVATE_KGAME) << "Player "<< id << "not found...asking user to create one";
366  newplayer=createPlayer(rtti,iovalue,isvirtual);
367  //Q_EMIT signalCreatePlayer(newplayer,rtti,iovalue,isvirtual,this);
368  }
369  /*
370  if (!newplayer)
371  {
372  qCWarning(GAMES_PRIVATE_KGAME) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! ";
373  newplayer=new KPlayer;
374  }
375  else
376  {
377  qCDebug(GAMES_PRIVATE_KGAME) << " USER Player" << newplayer << "done player->rtti=" << newplayer->rtti() << "rtti=" << rtti;
378  }
379  */
380  newplayer->load(stream);
381  if (isvirtual)
382  {
383  newplayer->setVirtual(true);
384  }
385  return newplayer;
386 }
387 
388 // ----------------- Player handling -----------------------
389 
390 KPlayer * KGame::findPlayer(quint32 id) const
391 {
392  for ( KGamePlayerList::iterator it = d->mPlayerList.begin(); it!=d->mPlayerList.end();++it )
393  {
394  if ((*it)->id() == id)
395  {
396  return *it;
397  }
398  }
399  for ( KGamePlayerList::iterator it = d->mInactivePlayerList.begin(); it!=d->mInactivePlayerList.end();++it )
400  {
401  if ((*it)->id() == id)
402  {
403  return *it;
404  }
405  }
406  return nullptr;
407 }
408 
409 // it is necessary that addPlayer and systemAddPlayer are called in the same
410 // order. Ie if addPlayer(foo) followed by addPlayer(bar) is called, you must
411 // not call systemAddPlayer(bar) followed by systemAddPlayer(foo), as the
412 // mAddPlayerList would get confused. Should be no problem as long as comServer
413 // and the clients are working correctly.
414 // BUT: if addPlayer(foo) does not arrive by any reason while addPlayer(bar)
415 // does, we would be in trouble...
416 bool KGame::addPlayer(KPlayer* newplayer)
417 {
418  qCDebug(GAMES_PRIVATE_KGAME) << ": " << "; maxPlayers=" << maxPlayers() << "playerCount=" << playerCount();
419  if (!newplayer)
420  {
421  qCWarning(GAMES_PRIVATE_KGAME) << "trying to add NULL player in KGame::addPlayer()";
422  return false;
423  }
424 
425  if (maxPlayers() >= 0 && (int)playerCount() >= maxPlayers())
426  {
427  qCWarning(GAMES_PRIVATE_KGAME) << "cannot add more than" << maxPlayers() << "players - deleting...";
428  return false;
429  }
430 
431  if (newplayer->id() == 0)
432  {
433  d->mUniquePlayerNumber++;
434  newplayer->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber, gameId()));
435  qCDebug(GAMES_PRIVATE_KGAME) << "NEW!!! player" << newplayer << "now has id" << newplayer->id();
436  }
437  else
438  {
439  // this could happen in games which use their own ID management by certain
440  // reasons. that is NOT recommended
441  qCDebug(GAMES_PRIVATE_KGAME) << "player" << newplayer << "already has an id:" << newplayer->id();
442  }
443 
444  QByteArray buffer;
445  QDataStream stream(&buffer,QIODevice::WriteOnly);
446  // We distinguish here what policy we have
447  if (policy()==PolicyLocal || policy()==PolicyDirty)
448  {
449  if ( !systemAddPlayer(newplayer) )
450  return false;
451  }
452  if (policy()==PolicyClean || policy()==PolicyDirty)
453  {
454  savePlayer(stream,newplayer);
455  // Store the player for delayed clean adding
456  if (policy()==PolicyClean)
457  {
458  d->mAddPlayerList.enqueue(newplayer);
459  }
460  sendSystemMessage(stream,(int)KGameMessage::IdAddPlayer, 0);
461  }
462  return true;
463 }
464 
466 {
467  if (!newplayer)
468  {
469  qCWarning(GAMES_PRIVATE_KGAME) << "trying to add NULL player in KGame::systemAddPlayer()";
470  return false ;
471  }
472  if (newplayer->id() == 0)
473  {
474  qCWarning(GAMES_PRIVATE_KGAME) << "player" << newplayer << "has no ID";
475  }
476 
477  if (findPlayer(newplayer->id()))
478  {
479  qCCritical(GAMES_PRIVATE_KGAME) << "ERROR: Double adding player !!!!! NOT GOOD !!!!!! " << newplayer->id() << "...I delete it again";
480  delete newplayer;
481  return false;
482  }
483  else
484  {
485  qCDebug(GAMES_PRIVATE_KGAME) << "Trying to add player" << newplayer <<" maxPlayers="<<maxPlayers()<<" playerCount="<<playerCount();
486  // Add the player to the game
487  d->mPlayerList.append(newplayer);
488  newplayer->setGame(this);
489  qCDebug(GAMES_PRIVATE_KGAME) << "Player: isVirtual=" << newplayer->isVirtual();
490  qCDebug(GAMES_PRIVATE_KGAME) << " id=" << newplayer->id() << " #Players="
491  << d->mPlayerList.count() << "added" << newplayer
492  << " (virtual=" << newplayer->isVirtual() << ")";
493  Q_EMIT signalPlayerJoinedGame(newplayer);
494  }
495  return true;
496 }
497 
498 // Called by the KPlayer destructor
500 {
501  qCDebug(GAMES_PRIVATE_KGAME) << ": id (" << player->id() << ") to be removed" << player;
502 
503  if (policy()==PolicyLocal || policy()==PolicyDirty)
504  {
505  systemRemovePlayer(player,false);
506  }
507  if (policy()==PolicyClean || policy()==PolicyDirty)
508  {
509  if (!player->isVirtual())
510  {
511  qCDebug(GAMES_PRIVATE_KGAME) << ": sending IdRemovePlayer "<<player->id();
512  sendSystemMessage(player->id(), KGameMessage::IdRemovePlayer, 0);
513  }
514  }
515 }
516 
517 bool KGame::removePlayer(KPlayer * player, quint32 receiver)
518 {//transmit to all clients, or to receiver only
519  if (!player)
520  {
521  qCWarning(GAMES_PRIVATE_KGAME) << "trying to remove NULL player in KGame::removePlayer( )" ;
522  return false;
523  }
524  qCDebug(GAMES_PRIVATE_KGAME) << ": id (" << player->id() << ") to be removed" << player;
525 
526  if (policy()==PolicyLocal || policy()==PolicyDirty)
527  {
528  systemRemovePlayer(player,true);
529  return true; // player is gone
530  }
531  if (policy()==PolicyClean || policy()==PolicyDirty)
532  {
533  qCDebug(GAMES_PRIVATE_KGAME) << ": sending IdRemovePlayer "<<player->id();
534  sendSystemMessage(player->id(),KGameMessage::IdRemovePlayer, receiver);
535  }
536  return true;
537  // we will receive the message in networkTransmission()
538 }
539 
540 void KGame::systemRemovePlayer(KPlayer* player,bool deleteit)
541 {
542  qCDebug(GAMES_PRIVATE_KGAME) ;
543  if (!player)
544  {
545  qCWarning(GAMES_PRIVATE_KGAME) << "cannot remove NULL player";
546  return;
547  }
548  systemRemove(player,deleteit);
549 
550  if (gameStatus()==(int)Run && playerCount()<minPlayers())
551  {
552  qCWarning(GAMES_PRIVATE_KGAME) << ": not enough players, PAUSING game\n";
553  setGameStatus(Pause);
554  }
555 }
556 
557 bool KGame::systemRemove(KPlayer* p,bool deleteit)
558 {
559  if (!p)
560  {
561  qCWarning(GAMES_PRIVATE_KGAME) << "cannot remove NULL player";
562  return false;
563  }
564  bool result;
565  qCDebug(GAMES_PRIVATE_KGAME) << ": Player (" << p->id() << ") to be removed" << p;
566 
567  if (d->mPlayerList.count() == 0)
568  {
569  result = false;
570  }
571  else
572  {
573 
574  result = d->mPlayerList.removeAll(p);
575  }
576 
578 
579  p->setGame(nullptr);
580  if (deleteit)
581  {
582  delete p;
583  }
584 
585  return result;
586 }
587 
589 {
590  if (!player)
591  {
592  return false;
593  }
594  qCDebug(GAMES_PRIVATE_KGAME) << "Inactivate player" << player->id();
595 
596  if (policy()==PolicyLocal || policy()==PolicyDirty)
597  {
598  if ( !systemInactivatePlayer(player) )
599  return false;
600  }
601  if (policy()==PolicyClean || policy()==PolicyDirty)
602  {
603  sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer);
604  }
605 
606  return true;
607 }
608 
610 {
611  if (!player || !player->isActive())
612  {
613  return false;
614  }
615  qCDebug(GAMES_PRIVATE_KGAME) << "Inactivate player" << player->id();
616 
617  int pid=player->id();
618  // Virtual players cannot be deactivated. They will be removed
619  if (player->isVirtual())
620  {
621  systemRemovePlayer(player,true);
622  return false; // don't touch player after this!
623  }
624  else
625  {
626  d->mPlayerList.removeAll(player);
627  d->mInactivePlayerList.prepend(player);
628  player->setActive(false);
629  }
631  if (isAdmin())
632  {
633  d->mInactiveIdList.prepend(pid);
634  }
635  return true;
636 }
637 
639 {
640  if (!player)
641  {
642  return false;
643  }
644  qCDebug(GAMES_PRIVATE_KGAME) << ": activate" << player->id();
645  if (policy()==PolicyLocal || policy()==PolicyDirty)
646  {
647  if ( !systemActivatePlayer(player) )
648  return false;
649  }
650  if (policy()==PolicyClean || policy()==PolicyDirty )
651  {
652  sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer);
653  }
654  return true;
655 }
656 
658 {
659  if (!player || player->isActive())
660  {
661  return false;
662  }
663  qCDebug(GAMES_PRIVATE_KGAME) << ": activate" << player->id();
664 
665  d->mInactivePlayerList.removeAll(player);
666  player->setActive(true);
667  if ( !addPlayer(player) ) // player is gone
668  return false;
669 
670  if (isAdmin())
671  {
672  d->mInactiveIdList.removeAll(player->id());
673  }
674  return true;
675 }
676 
677 // -------------------- Properties ---------------------------
678 
679 void KGame::setMaxPlayers(uint maxnumber)
680 { if (isAdmin()) { d->mMaxPlayer.changeValue(maxnumber); } }
681 
682 void KGame::setMinPlayers(uint minnumber)
683 { if (isAdmin()) { d->mMinPlayer.changeValue(minnumber); } }
684 
685 uint KGame::minPlayers() const
686 { return d->mMinPlayer.value(); }
687 
688 int KGame::maxPlayers() const
689 { return d->mMaxPlayer.value(); }
690 
691 uint KGame::playerCount() const
692 { return d->mPlayerList.count(); }
693 
694 int KGame::gameStatus() const
695 { return d->mGameStatus.value(); }
696 
697 bool KGame::isRunning() const
698 { return d->mGameStatus.value() == Run; }
699 
701 { return d->mProperties; }
702 
703 
705 { return &d->mInactivePlayerList; }
706 
708 { return &d->mInactivePlayerList; }
709 
711 { return &d->mPlayerList; }
712 
714 { return &d->mPlayerList; }
715 
716 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(7, 3)
717 KRandomSequence* KGame::random() const
718 { return d->mRandom; }
719 #endif
720 
721 bool KGame::sendPlayerInput(QDataStream &msg, KPlayer *player, quint32 sender)
722 {
723  if (!player)
724  {
725  qCCritical(GAMES_PRIVATE_KGAME) << ": NULL player";
726  return false;
727  }
728  if (!isRunning())
729  {
730  qCCritical(GAMES_PRIVATE_KGAME) << ": game not running";
731  return false;
732  }
733 
734  qCDebug(GAMES_PRIVATE_KGAME) << ": transmitting playerInput over network";
735  sendSystemMessage(msg, (int)KGameMessage::IdPlayerInput, player->id(), sender);
736  return true;
737 }
738 
740 {
741  if (!player)
742  {
743  qCCritical(GAMES_PRIVATE_KGAME) << ": NULL player";
744  return false;
745  }
746  if (!isRunning())
747  {
748  qCCritical(GAMES_PRIVATE_KGAME) << ": game not running";
749  return false;
750  }
751  qCDebug(GAMES_PRIVATE_KGAME) << "KGame: Got playerInput from messageServer... sender:" << sender;
752  if (playerInput(msg,player))
753  {
754  playerInputFinished(player);
755  }
756  else
757  {
758  qCDebug(GAMES_PRIVATE_KGAME) <<": switching off player input";
759  // TODO: (MH 03-2003): We need an return option from playerInput so that
760  // the player's is not automatically disabled here
761  if (!player->asyncInput())
762  {
763  player->setTurn(false); // in turn based games we have to switch off input now
764  }
765  }
766  return true;
767 }
768 
769 
771 {
772  if ( !player )
773  return nullptr;
774 
775  qCDebug(GAMES_PRIVATE_KGAME) <<"player input finished for "<<player->id();
776  // Check for game over and if not allow the next player to move
777  int gameOver = 0;
778  if (gameSequence())
779  {
780  gameSequence()->setCurrentPlayer(player);
781  }
782 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(3, 2)
783  gameOver = checkGameOver(player);
784 #else
785  gameOver = gameSequence()->checkGameOver(player);
786 #endif
787 if (gameOver!=0)
788  {
789  player->setTurn(false);
790  setGameStatus(End);
791  Q_EMIT signalGameOver(gameOver,player,this);
792  }
793  else if (!player->asyncInput())
794  {
795  player->setTurn(false); // in turn based games we have to switch off input now
796  if (gameSequence())
797  {
798 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(3, 2)
800 #else
802  QTimer::singleShot(0, gameSequence, [gameSequence]() {
803  gameSequence->nextPlayer(gameSequence->currentPlayer());
804  });
805 #endif
806  }
807  }
808  return player;
809 }
810 
811 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(3, 2)
812 // Per default we do not do anything
814 {
815  if (gameSequence())
816  {
817  return gameSequence()->checkGameOver(player);
818  }
819  return 0;
820 }
821 #endif
822 
824 {
825  delete d->mGameSequence;
826  d->mGameSequence = sequence;
827  if (d->mGameSequence)
828  {
829  d->mGameSequence->setGame(this);
830  }
831 }
832 
834 {
835  return d->mGameSequence;
836 }
837 
838 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(3, 2)
840 {
841  if (gameSequence())
842  {
843  // we don't call gameSequence->nextPlayer() to keep old code working
844  nextPlayer(gameSequence()->currentPlayer());
845  }
846 }
847 #endif
848 
849 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(3, 2)
850 KPlayer *KGame::nextPlayer(KPlayer *last,bool exclusive)
851 {
852  if (gameSequence())
853  {
854  return gameSequence()->nextPlayer(last, exclusive);
855  }
856  return nullptr;
857 }
858 #endif
859 
860 void KGame::setGameStatus(int status)
861 {
862  qCDebug(GAMES_PRIVATE_KGAME) << ": GAMESTATUS CHANGED to" << status;
863  if (status==(int)Run && playerCount()<minPlayers())
864  {
865  qCDebug(GAMES_PRIVATE_KGAME) << ": not enough players, pausing game\n";
866  status=Pause;
867  }
868  d->mGameStatus = status;
869 }
870 
871 void KGame::networkTransmission(QDataStream &stream, int msgid, quint32 receiver, quint32 sender, quint32 /*clientID*/)
872 {//clientID is unused
873  // message targets a playerobject. If we find it we forward the message to the
874  // player. Otherwise we proceed here and hope the best that the user processes
875  // the message
876 
877 // qCDebug(GAMES_PRIVATE_KGAME) << ": we="<<(int)gameId()<<" id="<<msgid<<" recv=" << receiver << "sender=" << sender;
878 
879 
880  // *first* notice the game that something has changed - so no return prevents
881  // this
882  Q_EMIT signalMessageUpdate(msgid, receiver, sender);
883  if (KGameMessage::isPlayer(receiver))
884  {
885  //qCDebug(GAMES_PRIVATE_KGAME) << "message id" << msgid << "seems to be for a player ("<<active=p->isActive()<<" recv="<< receiver;
886  KPlayer *p=findPlayer(receiver);
887  if (p && p->isActive())
888  {
889  p->networkTransmission(stream,msgid,sender);
890  return;
891  }
892  if (p)
893  {
894  qCDebug(GAMES_PRIVATE_KGAME) << "player is here but not active";
895  }
896  else
897  {
898  qCDebug(GAMES_PRIVATE_KGAME) << "no player found";
899  }
900  }
901  // If it is not for a player it is meant for us!!!! Otherwise the
902  // gamenetwork would not have passed the message to us!
903 
904  // GameProperties processed
905  if (d->mProperties->processMessage(stream, msgid, sender == gameId()))
906  {
907 // qCDebug(GAMES_PRIVATE_KGAME) << "KGame: message taken by property - returning";
908  return ;
909  }
910 
911  switch(msgid)
912  {
913  case KGameMessage::IdSetupGame: // Client: First step in setup game
914  {
915  qint16 v;
916  qint32 c;
917  stream >> v >> c;
918  qCDebug(GAMES_PRIVATE_KGAME) << " ===================> (Client) " << ": Got IdSetupGame ==================";
919  qCDebug(GAMES_PRIVATE_KGAME) << "our game id is" << gameId() << "Lib version=" << v << "App Cookie=" << c;
920  // Verify identity of the network partners
921  if (c!=cookie())
922  {
923  qCCritical(GAMES_PRIVATE_KGAME) << "IdGameSetup: Negotiate Game: cookie mismatch I'am="<<cookie()<<" master="<<c;
924  sendError(KGameError::Cookie, KGameError::errCookie(cookie(), c));
925  disconnect(); // disconnect from master
926  }
927  else if (v!=KGameMessage::version())
928  {
929  sendError(KGameError::Version, KGameError::errVersion(v));
930  disconnect(); // disconnect from master
931  }
932  else
933  {
934  setupGame(sender);
935  }
936  qCDebug(GAMES_PRIVATE_KGAME) << "========== (Client) Setup game done\n";
937  }
938  break;
939  case KGameMessage::IdSetupGameContinue: // Master: second step in game setup
940  {
941  qCDebug(GAMES_PRIVATE_KGAME) << "=====>(Master) " << " - IdSetupGameContinue";
942  setupGameContinue(stream, sender);
943  }
944  break;
945  case KGameMessage::IdActivatePlayer: // Activate Player
946  {
947  int id;
948  stream >> id;
949  qCDebug(GAMES_PRIVATE_KGAME) << "Got IdActivatePlayer id=" << id;
950  if (sender!=gameId() || policy()!=PolicyDirty)
951  {
953  }
954  }
955  break;
956  case KGameMessage::IdInactivatePlayer: // Inactivate Player
957  {
958  int id;
959  stream >> id;
960  qCDebug(GAMES_PRIVATE_KGAME) << "Got IdInactivatePlayer id=" << id;
961  if (sender!=gameId() || policy()!=PolicyDirty)
962  {
964  }
965  }
966  break;
967  case KGameMessage::IdAddPlayer:
968  {
969  qCDebug(GAMES_PRIVATE_KGAME) << ": Got IdAddPlayer";
970  if (sender!=gameId() || policy()!=PolicyDirty)
971  {
972  KPlayer *newplayer=nullptr;
973  // We sent the message so the player is already available
974  if (sender==gameId())
975  {
976  qCDebug(GAMES_PRIVATE_KGAME) << "dequeue previously added player";
977  newplayer = d->mAddPlayerList.dequeue();
978  }
979  else
980  {
981  newplayer=loadPlayer(stream,true);
982  }
983  systemAddPlayer(newplayer);// the final, local, adding
984  //systemAddPlayer(stream);
985  }
986  }
987  break;
988  case KGameMessage::IdRemovePlayer: // Client should delete player id
989  {
990  int id;
991  stream >> id;
992  qCDebug(GAMES_PRIVATE_KGAME) << ": Got IdRemovePlayer" << id;
993  KPlayer *p=findPlayer(id);
994  if (p)
995  {
996  // Otherwise the player is already removed
997  if (sender!=gameId() || policy()!=PolicyDirty)
998  {
999  systemRemovePlayer(p,true);
1000  }
1001  }
1002  else
1003  {
1004  qCWarning(GAMES_PRIVATE_KGAME) << "Cannot find player" << id;
1005  }
1006  }
1007  break;
1008  case KGameMessage::IdGameLoad:
1009  {
1010  qCDebug(GAMES_PRIVATE_KGAME) << "====> (Client) " << ": Got IdGameLoad";
1011  loadgame(stream,true,false);
1012  }
1013  break;
1014  case KGameMessage::IdGameSetupDone:
1015  {
1016  int cid;
1017  stream >> cid;
1018  qCDebug(GAMES_PRIVATE_KGAME) << "====> (CLIENT) " << ": Got IdGameSetupDone for client "
1019  << cid << "we are =" << gameId();
1020  sendSystemMessage(gameId(), KGameMessage::IdGameConnected, 0);
1021  }
1022  break;
1023  case KGameMessage::IdGameConnected:
1024  {
1025  int cid;
1026  stream >> cid;
1027  qCDebug(GAMES_PRIVATE_KGAME) << "====> (ALL) " << ": Got IdGameConnected for client "<< cid << "we are =" << gameId();
1028  Q_EMIT signalClientJoinedGame(cid,this);
1029  }
1030  break;
1031 
1032 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(7, 3)
1033  case KGameMessage::IdSyncRandom: // Master forces a new random seed on us
1034  {
1035  int newseed;
1036  stream >> newseed;
1037  qCDebug(GAMES_PRIVATE_KGAME) << "CLIENT: setting random seed to" << newseed;
1038  d->mRandom->setSeed(newseed);
1039  }
1040  break;
1041 #endif
1042 
1043  case KGameMessage::IdDisconnect:
1044  {
1045  // if we disconnect we *always* start a local game.
1046  // this could lead into problems if we just change the message server
1047  if (sender != gameId())
1048  {
1049  qCDebug(GAMES_PRIVATE_KGAME) << "client" << sender << "leaves game";
1050  return;
1051  }
1052  qCDebug(GAMES_PRIVATE_KGAME) << "leaving the game";
1053  // start a new local game
1054  // no other client is by default connected to this so this call should be
1055  // enough
1056  setMaster();
1057  }
1058  break;
1059  default:
1060  {
1061  if (msgid < KGameMessage::IdUser)
1062  {
1063  qCCritical(GAMES_PRIVATE_KGAME) << "incorrect message id" << msgid << " - emit anyway";
1064  }
1065  qCDebug(GAMES_PRIVATE_KGAME) << ": User data msgid" << msgid;
1066  Q_EMIT signalNetworkData(msgid - KGameMessage::IdUser,((QBuffer*)stream.device())->readAll(),receiver,sender);
1067  }
1068  break;
1069  }
1070 
1071 }
1072 
1073 // called by the IdSetupGameContinue Message - MASTER SIDE
1074 // Here the master needs to decide which players can take part at the game
1075 // and which will be deactivated
1076 void KGame::setupGameContinue(QDataStream& stream, quint32 sender)
1077 {
1078  KPlayer *player;
1079  qint32 cnt;
1080  int i;
1081  stream >> cnt;
1082 
1083  QList<int> inactivateIds;
1084 
1085  KGamePlayerList newPlayerList;
1086  for (i=0;i<cnt;++i)
1087  {
1088  player=loadPlayer(stream,true);
1089  qCDebug(GAMES_PRIVATE_KGAME) << "Master got player" << player->id() <<" rawgame=" << KGameMessage::rawGameId(player->id()) << "from sender" << sender;
1090  if (KGameMessage::rawGameId(player->id()) != sender)
1091  {
1092  qCCritical(GAMES_PRIVATE_KGAME) << "Client tries to add player with wrong game id - cheat possible";
1093  }
1094  else
1095  {
1096  newPlayerList.append(player);
1097  qCDebug(GAMES_PRIVATE_KGAME) << "newplayerlist appended" << player->id();
1098  }
1099  }
1100 
1101  newPlayersJoin(playerList(),&newPlayerList,inactivateIds);
1102 
1103 
1104  qCDebug(GAMES_PRIVATE_KGAME) << "Master calculates how many players to activate client has cnt=" << cnt;
1105  qCDebug(GAMES_PRIVATE_KGAME) << "The game has" << playerCount() << "active players";
1106  qCDebug(GAMES_PRIVATE_KGAME) << "The user deactivated "<< inactivateIds.count() << "player already";
1107  qCDebug(GAMES_PRIVATE_KGAME) << "MaxPlayers for this game is" << maxPlayers();
1108 
1109  // Do we have too many players? (After the programmer disabled some?)
1110  // MH: We cannot use have player here as it CHANGES in the loop
1111  // int havePlayers = cnt+playerCount()-inactivateIds.count();
1112  qCDebug(GAMES_PRIVATE_KGAME) << "havePlayers" << cnt+playerCount()-inactivateIds.count();
1113  while (maxPlayers() > 0 && maxPlayers() < (int)(cnt+playerCount() - inactivateIds.count()))
1114  {
1115  qCDebug(GAMES_PRIVATE_KGAME) << " Still to deactivate "
1116  << (int)(cnt+playerCount()-inactivateIds.count())-(int)maxPlayers()
1117 ;
1118  KPlayer *currentPlayer=nullptr;
1119  int currentPriority=0x7fff; // MAX_UINT (16bit?) to get the maximum of the list
1120  // find lowest network priority which is not yet in the newPlayerList
1121  // do this for the new players
1122  for ( KGamePlayerList::iterator it = newPlayerList.begin(); it!=newPlayerList.end();++it )
1123  {
1124  KPlayer* player = *it;
1125  // Already in the list
1126  if (inactivateIds.indexOf(player->id())!=-1)
1127  {
1128  continue;
1129  }
1130  if (player->networkPriority()<currentPriority)
1131  {
1132  currentPriority=player->networkPriority();
1133  currentPlayer=player;
1134  }
1135  }
1136 
1137  // find lowest network priority which is not yet in the newPlayerList
1138  // Do this for the network players
1139  for ( KGamePlayerList::iterator it = d->mPlayerList.begin(); it!=d->mPlayerList.end();++it )
1140  {
1141  KPlayer* player = *it;
1142  // Already in the list
1143  if (inactivateIds.indexOf(player->id())!=-1)
1144  {
1145  continue;
1146  }
1147  if (player->networkPriority()<currentPriority)
1148  {
1149  currentPriority=player->networkPriority();
1150  currentPlayer=player;
1151  }
1152  }
1153 
1154  // add it to inactivateIds
1155  if (currentPlayer)
1156  {
1157  qCDebug(GAMES_PRIVATE_KGAME) << "Marking player" << currentPlayer->id() << "for inactivation";
1158  inactivateIds.append(currentPlayer->id());
1159  }
1160  else
1161  {
1162  qCCritical(GAMES_PRIVATE_KGAME) << "Couldn't find a player to deactivate. That is not so good...";
1163  break;
1164  }
1165  }
1166 
1167  qCDebug(GAMES_PRIVATE_KGAME) << "Altogether deactivated" << inactivateIds.count() << "players";
1168 
1170  for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it )
1171  {
1172  int pid=*it;
1173  qCDebug(GAMES_PRIVATE_KGAME) << "pid=" << pid;
1174  }
1175 
1176  // Now deactivate the network players from the inactivateId list
1177  //QValueList<int>::Iterator it;
1178  for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it )
1179  {
1180  int pid=*it;
1181  if (KGameMessage::rawGameId(pid) == sender)
1182  {
1183  continue; // client's player
1184  }
1185  qCDebug(GAMES_PRIVATE_KGAME) << " -> the network needs to deactivate" << pid;
1186  player=findPlayer(pid);
1187  if (player)
1188  {
1189  // We have to make REALLY sure that the player is gone. With any policy
1190  if (systemInactivatePlayer(player) && policy()!=PolicyLocal)
1191  {
1192  sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer);
1193  } else
1194  player = nullptr;
1195  }
1196  else
1197  {
1198  qCCritical(GAMES_PRIVATE_KGAME) << "We should deactivate a player, but cannot find it...not good.";
1199  }
1200  }
1201 
1202  // Now send out the player list which the client can activate
1203  for ( KGamePlayerList::iterator it = newPlayerList.begin(); it!=newPlayerList.end();++it )
1204  {
1205  KPlayer* player = *it;
1206  qCDebug(GAMES_PRIVATE_KGAME) << "newplayerlist contains" << player->id();
1207  // Only activate what is not in the list
1208  if (inactivateIds.indexOf(player->id())!=-1)
1209  {
1210  continue;
1211  }
1212  qCDebug(GAMES_PRIVATE_KGAME) << " -> the client can ******** reactivate ******** " << player->id();
1213  sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer, sender);
1214  }
1215 
1216  // Save the game over the network
1217  QByteArray bufferS;
1218  QDataStream streamS(&bufferS,QIODevice::WriteOnly);
1219  // Save game over netowrk and save players
1220  savegame(streamS,true,true);
1221  sendSystemMessage(streamS,KGameMessage::IdGameLoad,sender);
1222 
1223 
1224  // Only to the client first , as the client will add players
1225  sendSystemMessage(sender, KGameMessage::IdGameSetupDone, sender);
1226 
1227  //Finally delete content of the newPlayerList
1228  qDeleteAll(newPlayerList);
1229  newPlayerList.clear();
1230 }
1231 
1232 // called by the IdSetupGame Message - CLIENT SIDE
1233 // Client needs to prepare for network transfer
1234 void KGame::setupGame(quint32 sender)
1235 {
1236  QByteArray bufferS;
1237  QDataStream streamS(&bufferS,QIODevice::WriteOnly);
1238 
1239  // Deactivate all players
1240  KGamePlayerList mTmpList(d->mPlayerList); // we need copy otherwise the removal crashes
1241  qint32 cnt=mTmpList.count();
1242  qCDebug(GAMES_PRIVATE_KGAME) << "Client: playerlistcount=" << d->mPlayerList.count() << "tmplistcout=" << cnt;
1243 
1244  streamS << cnt;
1245 
1246  KGamePlayerList::iterator it = mTmpList.begin();
1247  KPlayer *player;
1248  while (it!=mTmpList.end())
1249  {
1250  player=*it;
1251  ++it;
1252  --cnt;
1253 
1254  if (!systemInactivatePlayer(player))
1255  continue; // player is gone
1256 
1257  // Give the new game id to all players (which are inactivated now)
1258  player->setId(KGameMessage::createPlayerId(player->id(),gameId()));
1259 
1260  // Save it for the master to decide what to do
1261  savePlayer(streamS,player);
1262  }
1263  if (d->mPlayerList.count() > 0 || cnt!=0)
1264  {
1265  qCWarning(GAMES_PRIVATE_KGAME) << "KGame::setupGame(): Player list is not empty! or cnt!=0=" <<cnt;
1266  abort();
1267  }
1268 
1269  sendSystemMessage(streamS,KGameMessage::IdSetupGameContinue,sender);
1270 }
1271 
1272 #if KDEGAMESPRIVATE_BUILD_DEPRECATED_SINCE(7, 3)
1273 // unused by KGame
1275 {
1276  int newseed=(int)d->mRandom->getLong(65535);
1277  sendSystemMessage(newseed,KGameMessage::IdSyncRandom); // Broadcast
1278  d->mRandom->setSeed(newseed);
1279 }
1280 #endif
1281 
1283 {
1285  qCDebug(GAMES_PRIVATE_KGAME) << "------------------- KGAME -------------------------";
1286  qCDebug(GAMES_PRIVATE_KGAME) << "this: " << this;
1287  qCDebug(GAMES_PRIVATE_KGAME) << "uniquePlayer " << d->mUniquePlayerNumber;
1288  qCDebug(GAMES_PRIVATE_KGAME) << "gameStatus " << gameStatus();
1289  qCDebug(GAMES_PRIVATE_KGAME) << "MaxPlayers : " << maxPlayers();
1290  qCDebug(GAMES_PRIVATE_KGAME) << "NoOfPlayers : " << playerCount();
1291  qCDebug(GAMES_PRIVATE_KGAME) << "NoOfInactive: " << d->mInactivePlayerList.count();
1292  qCDebug(GAMES_PRIVATE_KGAME) << "---------------------------------------------------";
1293 }
1294 
1295 void KGame::slotClientConnected(quint32 clientID)
1296 {
1297  if (isAdmin())
1298  {
1299  negotiateNetworkGame(clientID);
1300  }
1301 }
1302 
1303 void KGame::slotServerDisconnected() // Client side
1304 {
1305  qCDebug(GAMES_PRIVATE_KGAME) << "======= SERVER DISCONNECT =======";
1306  qCDebug(GAMES_PRIVATE_KGAME) << "+++ (CLIENT)++++++++" << ": our GameID="<<gameId();
1307 
1308  int oldgamestatus=gameStatus();
1309 
1310 
1311  KGamePlayerList removeList;
1312  qCDebug(GAMES_PRIVATE_KGAME) << "Playerlist of client=" << d->mPlayerList.count() << "count";
1313  qCDebug(GAMES_PRIVATE_KGAME) << "Inactive Playerlist of client=" << d->mInactivePlayerList.count() << "count";
1314  for ( KGamePlayerList::iterator it = d->mPlayerList.begin(); it!=d->mPlayerList.end();++it )
1315  {
1316  KPlayer* player = *it;
1317  // TODO: CHECK: id=0, could not connect to server in the first place??
1318  if (KGameMessage::rawGameId(player->id()) != gameId() && gameId()!=0)
1319  {
1320  qCDebug(GAMES_PRIVATE_KGAME) << "Player" << player->id() << "belongs to a removed game";
1321  removeList.append(player);
1322  }
1323  }
1324 
1325  for ( KGamePlayerList::iterator it = removeList.begin(); it!=removeList.end();++it )
1326  {
1327  KPlayer* player = *it;
1328  bool remove = true;
1329  Q_EMIT signalReplacePlayerIO(player, &remove);
1330  if (remove)
1331  {
1332  qCDebug(GAMES_PRIVATE_KGAME) << " ---> Removing player" << player->id();
1333  systemRemovePlayer(player,true); // no network necessary
1334  }
1335  }
1336 
1337  setMaster();
1338  qCDebug(GAMES_PRIVATE_KGAME) << "our game id is after setMaster" << gameId();
1339 
1340  KGamePlayerList mReList(d->mInactivePlayerList);
1341  for ( KGamePlayerList::iterator it = mReList.begin(); it!=mReList.end();++it )
1342  {
1343  KPlayer* player = *it;
1344  // TODO ?check for priority? Sequence should be ok
1345  if ((int)playerCount()<maxPlayers() || maxPlayers()<0)
1346  {
1347  systemActivatePlayer(player);
1348  }
1349  }
1350  qCDebug(GAMES_PRIVATE_KGAME) << "Players activated player-cnt=" << playerCount();
1351 
1352  for ( KGamePlayerList::iterator it = d->mPlayerList.begin(); it!=d->mPlayerList.end();++it )
1353  {
1354  KPlayer* player = *it;
1355  int oldid=player->id();
1356  d->mUniquePlayerNumber++;
1357  player->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber,gameId()));
1358  qCDebug(GAMES_PRIVATE_KGAME) << "Player id" << oldid <<" changed to" << player->id() << "as we are now local";
1359  }
1360  // TODO clear inactive lists ?
1361  Debug();
1362  for ( KGamePlayerList::iterator it = d->mPlayerList.begin(); it!=d->mPlayerList.end();++it )
1363  {
1364  KPlayer* player = *it;
1365  player->Debug();
1366  }
1367  qCDebug(GAMES_PRIVATE_KGAME) << "+++++++++++" << "DONE=";
1368  Q_EMIT signalClientLeftGame(0,oldgamestatus,this);
1369 }
1370 
1371 void KGame::slotClientDisconnected(quint32 clientID,bool /*broken*/) // server side
1372 {
1373  qCDebug(GAMES_PRIVATE_KGAME) << "++++(SERVER)+++++++" << "clientId=" << clientID;
1374 
1375  int oldgamestatus=gameStatus();
1376 
1377  KPlayer *player;
1378  KGamePlayerList removeList;
1379  qCDebug(GAMES_PRIVATE_KGAME) << "Playerlist of client=" << d->mPlayerList.count() << "count";
1380  for ( KGamePlayerList::iterator it = d->mPlayerList.begin(); it!=d->mPlayerList.end();++it )
1381  {
1382  KPlayer* player = *it;
1383  if (KGameMessage::rawGameId(player->id())==clientID)
1384  {
1385  qCDebug(GAMES_PRIVATE_KGAME) << "Player" << player->id() << "belongs to the removed game";
1386  removeList.append(player);
1387  }
1388  }
1389 
1390  for ( KGamePlayerList::iterator it = removeList.begin(); it!=removeList.end();++it )
1391  {
1392  KPlayer* player = *it;
1393  // try to replace the KGameIO first
1394  bool remove = true;
1395  Q_EMIT signalReplacePlayerIO(player, &remove);
1396  if (remove) {
1397  // otherwise (no new KGameIO) remove the player
1398  qCDebug(GAMES_PRIVATE_KGAME) << " ---> Removing player" << player->id();
1399  removePlayer(player,0);
1400  }
1401  }
1402 
1403  // Now add inactive players - sequence should be ok
1404  // TODO remove players from removed game
1405  for (int idx=0;idx<d->mInactiveIdList.count();idx++)
1406  {
1407  int it1 = d->mInactiveIdList.at(idx);
1408  player = findPlayer(it1);
1409  if (((int)playerCount() < maxPlayers() || maxPlayers() < 0) && player && KGameMessage::rawGameId(it1) != clientID)
1410  {
1411  activatePlayer(player);
1412  }
1413  }
1414  Q_EMIT signalClientLeftGame(clientID,oldgamestatus,this);
1415 }
1416 
1417 
1418 // -------------------- Synchronization -----------------------
1419 
1420 // this initializes a newly connected client.
1421 // we send the number of players (including type) as well as game status and
1422 // properties to the client. After the initialization has been completed both
1423 // clients should have the same status (ie players, properties, etc)
1424 void KGame::negotiateNetworkGame(quint32 clientID)
1425 {
1426  qCDebug(GAMES_PRIVATE_KGAME) << "===========================" << ": clientID=" << clientID << " =========================== ";
1427  if (!isAdmin())
1428  {
1429  qCCritical(GAMES_PRIVATE_KGAME) << ": Serious WARNING..only gameAdmin should call this";
1430  return ;
1431  }
1432 
1433  QByteArray buffer;
1434  QDataStream streamGS(&buffer,QIODevice::WriteOnly);
1435 
1436  // write Game setup specific data
1437  //streamGS << (qint32)maxPlayers();
1438  //streamGS << (qint32)minPlayers();
1439 
1440  // send to the newly connected client *only*
1441  qint16 v=KGameMessage::version();
1442  qint32 c=cookie();
1443  streamGS << v << c;
1444  sendSystemMessage(streamGS, KGameMessage::IdSetupGame, clientID);
1445 }
1446 
1447 bool KGame::sendGroupMessage(const QByteArray &msg, int msgid, quint32 sender, const QString& group)
1448 {
1449 // AB: group must not be i18n'ed!! we should better use an id for group and use
1450 // a groupName() for the name // FIXME
1451 
1452  for ( KGamePlayerList::iterator it = d->mPlayerList.begin(); it!=d->mPlayerList.end();++it )
1453  {
1454  KPlayer* player = *it;
1455  if (player && player->group()==group)
1456  {
1457  sendMessage(msg,msgid,player->id(), sender);
1458  }
1459  }
1460  return true;
1461 }
1462 
1463 bool KGame::sendGroupMessage(const QDataStream &msg, int msgid, quint32 sender, const QString& group)
1464 { return sendGroupMessage(((QBuffer*)msg.device())->buffer(), msgid, sender, group); }
1465 
1466 bool KGame::sendGroupMessage(const QString& msg, int msgid, quint32 sender, const QString& group)
1467 {
1468  QByteArray buffer;
1469  QDataStream stream(&buffer, QIODevice::WriteOnly);
1470  stream << msg;
1471  return sendGroupMessage(stream, msgid, sender, group);
1472 }
1473 
1475 { return dataHandler()->addProperty(data); }
1476 
1477 bool KGame::sendPlayerProperty(int msgid, QDataStream& s, quint32 playerId)
1478 { return sendSystemMessage(s, msgid, playerId); }
1479 
1480 void KGame::sendProperty(int msgid, QDataStream& stream, bool* sent)
1481 {
1482  bool s = sendSystemMessage(stream, msgid);
1483  if (s)
1484  {
1485  *sent = true;
1486  }
1487 }
1488 
1490 {
1491  Q_EMIT signalPropertyChanged(me,this);
1492 }
1493 
1495 { return d->mProperties->find(id); }
1496 
1498 {
1499  return d->mPolicy;
1500 }
1501 void KGame::setPolicy(GamePolicy p,bool recursive)
1502 {
1503  // Set KGame policy
1504  d->mPolicy=p;
1505  if (recursive)
1506  {
1507  // Set all KGame property policy
1509 
1510  // Set all KPLayer (active or inactive) property policy
1511  for ( KGamePlayerList::iterator it = d->mPlayerList.begin(); it!=d->mPlayerList.end();++it )
1512  {
1513  (*it)->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
1514  }
1515  for ( KGamePlayerList::iterator it = d->mInactivePlayerList.begin(); it!=d->mInactivePlayerList.end();++it )
1516  {
1517  (*it)->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
1518  }
1519  }
1520 }
1521 
1522 /*
1523  * vim: et sw=2
1524  */
This class takes care of round or move management as well of the gameover condition.
Definition: kgamesequence.h:35
virtual bool reset()
Resets the game, i.e.
Definition: kgame.cpp:124
void setMaxPlayers(uint maxnumber)
Set the maximal number of players.
Definition: kgame.cpp:679
void clear()
void emitSignal(KGamePropertyBase *me)
Called by KGamePropertyHandler only! Internal function!
Definition: kgame.cpp:1489
bool sendMessage(const QByteArray &buffer, int msgid, quint32 receiver=0, quint32 sender=0)
Send a network message msg with a given message ID msgid to all clients.
void signalSavePrePlayers(QDataStream &stream)
The game will be saved to the given stream.
bool addPlayer(KPlayer *newplayer)
Note that KPlayer::save must be implemented properly, as well as KPlayer::rtti This will only send a ...
Definition: kgame.cpp:416
void lockDirectEmit()
Called by the KGame or KPlayer object or the handler itself to delay emitting of signals.
void setVirtual(bool v)
Definition: kplayer.cpp:241
quint32 id() const
Returns the id of the player.
Definition: kplayer.cpp:235
void signalConnectionBroken()
Our connection to the KMessageServer has broken.
virtual bool systemPlayerInput(QDataStream &msg, KPlayer *player, quint32 sender=0)
Called when a player input arrives from KMessageServer.
Definition: kgame.cpp:739
void signalSave(QDataStream &stream)
The game will be saved to the given stream.
GamePolicy policy() const
Definition: kgame.cpp:1497
bool systemAddPlayer(KPlayer *newplayer)
Adds a player to the game.
Definition: kgame.cpp:465
bool removePlayer(KPlayer *player)
Sends a message over the network, msgid=IdRemovePlayer.
Definition: kgame.h:223
virtual bool loadgame(QDataStream &stream, bool network, bool reset)
Load a saved game, from file OR network.
Definition: kgame.cpp:175
void sendError(int error, const QByteArray &message, quint32 receiver=0, quint32 sender=0)
Sends a network message.
void signalClientConnected(quint32 clientID)
This signal is emitted whenever the KMessageServer sends us a message that a new client connected...
bool setTurn(bool b, bool exclusive=true)
Sets whether this player is the next to turn.
Definition: kplayer.cpp:330
int calcIOValue()
Calculates a checksum over the IO devices.
Definition: kplayer.cpp:319
A collection class for KGameProperty objects.
QObject * sender() const const
bool systemInactivatePlayer(KPlayer *player)
inactivates player.
Definition: kgame.cpp:609
void signalLoadPrePlayers(QDataStream &stream)
The game will be loaded from the given stream.
void systemRemovePlayer(KPlayer *player, bool deleteit)
Removes a player from the game.
Definition: kgame.cpp:540
PropertyPolicy
The policy of the property.
virtual void negotiateNetworkGame(quint32 clientID)
This member function will transmit e.g.
Definition: kgame.cpp:1424
bool activatePlayer(KPlayer *player)
sends activate player: internal use only?
Definition: kgame.cpp:638
int gameStatus() const
returns the game status, ie running,pause,ended,...
Definition: kgame.cpp:694
void Debug()
Gives debug output of the game status.
Definition: kplayer.cpp:471
void signalClientLeftGame(int clientID, int oldgamestatus, KGame *me)
This signal is emitted after a network partner left the game (either by a broken connection or volunt...
virtual bool load(QDataStream &stream)
Loads properties from the datastream.
virtual bool playerInput(QDataStream &msg, KPlayer *player)=0
A player input occurred.
virtual void newPlayersJoin(KGamePlayerList *oldplayer, KGamePlayerList *newplayer, QList< int > &inactivate)
This virtual function can be overwritten for your own player management.
Definition: kgame.h:756
void setMinPlayers(uint minnumber)
Set the minimal number of players.
Definition: kgame.cpp:682
KGamePropertyHandler * dataHandler() const
Returns a pointer to the KGame property handler.
Definition: kgame.cpp:700
bool inactivatePlayer(KPlayer *player)
sends inactivate player: internal use only?
Definition: kgame.cpp:588
int cookie() const
Application cookie.
bool sendGroupMessage(const QByteArray &msg, int msgid, quint32 sender, const QString &group)
See KGameNetwork::sendMessage.
Definition: kgame.cpp:1447
void slotServerDisconnected()
This slot is called whenever the connection to the server is lost (ie the signal KGameNetwork::signal...
Definition: kgame.cpp:1303
bool isVirtual() const
Is this player a virtual player, i.e.
Definition: kplayer.cpp:244
KGame(int cookie=42, QObject *parent=nullptr)
Create a KGame object.
Definition: kgame.cpp:71
bool isNull() const const
void signalLoad(QDataStream &stream)
The game will be loaded from the given stream.
void savePlayers(QDataStream &stream, KGamePlayerList *list=nullptr)
Save the player list to a stream.
Definition: kgame.cpp:336
int indexOf(const T &value, int from) const const
GamePolicy
The policy of the property.
Definition: kgame.h:85
virtual int checkGameOver(KPlayer *player)
Check whether the game is over.
virtual bool savegame(QDataStream &stream, bool network, bool saveplayers)
Save a game, to file OR network.
Definition: kgame.cpp:289
void savePlayer(QDataStream &stream, KPlayer *player)
Prepare a player for being added.
Definition: kgame.cpp:327
int count(const T &value) const const
virtual int checkGameOver(KPlayer *player)
Definition: kgame.cpp:813
virtual bool save(QDataStream &stream)
Saves properties into the datastream.
void append(const T &value)
void setActive(bool v)
Set an player as active (true) or inactive (false)
Definition: kplayer.cpp:148
KPlayer * playerInputFinished(KPlayer *player)
Called after the player input is processed by the game.
Definition: kgame.cpp:770
bool sendPlayerProperty(int msgid, QDataStream &s, quint32 playerId)
This is called by KPlayer::sendProperty only! Internal function!
Definition: kgame.cpp:1477
KGamePlayerList * inactivePlayerList()
Returns a list of all inactive players.
Definition: kgame.cpp:704
bool isRunning() const
Is the game running.
Definition: kgame.cpp:697
virtual KPlayer * nextPlayer(KPlayer *last, bool exclusive=true)
Select the next player in a turn based game.
virtual bool load(QDataStream &stream)
Load a saved player, from file OR network.
Definition: kplayer.cpp:358
virtual const QString & group() const
Query the group the player belongs to.
Definition: kplayer.cpp:226
bool asyncInput() const
Query whether this player does asynchronous input.
Definition: kplayer.cpp:138
int networkPriority() const
Returns whether this player can be replaced by a network connection player.
Definition: kplayer.cpp:253
void signalPlayerJoinedGame(KPlayer *player)
a player joined the game
~KGame() override
Destructs the game.
Definition: kgame.cpp:111
KPlayer * findPlayer(quint32 id) const
Returns the player object for a given player id.
Definition: kgame.cpp:390
virtual bool save(QDataStream &stream)
Save a player to a file OR to network.
Definition: kplayer.cpp:384
void slotClientDisconnected(quint32 clientId, bool broken)
This slot is called whenever the connection to a client is lost (ie the signal KGameNetwork::signalCl...
Definition: kgame.cpp:1371
void signalLoadError(QDataStream &stream, bool network, int cookie, bool &result)
Is emitted if a game with a different version cookie is loaded.
virtual bool open(QIODevice::OpenMode mode) override
Base class for a game player.
Definition: kplayer.h:60
void signalGameOver(int status, KPlayer *current, KGame *me)
Is emitted after a call to gameOver() returns a non zero return code.
void setPolicy(KGamePropertyBase::PropertyPolicy p, bool userspace=true)
Set the policy for all kgame variables which are currently registered in the KGame property handler...
QList::iterator end()
Base class of KGameProperty.
Definition: kgameproperty.h:35
bool isAdmin() const
The admin of a game is the one who initializes newly connected clients using negotiateNetworkGame and...
void signalReplacePlayerIO(KPlayer *player, bool *remove)
When a client disconnects from the game usually all players from that client are removed.
void signalNetworkData(int msgid, const QByteArray &buffer, quint32 receiver, quint32 sender)
We got an user defined update message.
void signalPlayerLeftGame(KPlayer *player)
a player left the game because of a broken connection or so!
virtual void prepareNext()
Definition: kgame.cpp:839
void syncRandom()
synchronize the random numbers with all network clients not used by KGame - if it should be kept then...
Definition: kgame.cpp:1274
QString i18n(const char *text, const TYPE &arg...)
virtual bool load(QDataStream &stream, bool reset=true)
Load a saved game, from file OR network.
Definition: kgame.cpp:172
bool systemActivatePlayer(KPlayer *player)
activates player.
Definition: kgame.cpp:657
void networkTransmission(QDataStream &stream, int msgid, quint32 receiver, quint32 sender, quint32 clientID) override
This will either forward an incoming message to a specified player (see KPlayer::networkTransmission)...
Definition: kgame.cpp:871
The KGameNetwork class is the KGame class with network support.
Definition: kgamenetwork.h:38
KPlayer * loadPlayer(QDataStream &stream, bool isvirtual=false)
Load the player list from a stream.
Definition: kgame.cpp:358
void setGame(KGame *game)
sets the game the player belongs to.
Definition: kplayer.cpp:123
virtual bool sendPlayerInput(QDataStream &msg, KPlayer *player, quint32 sender=0)
Called by KPlayer to send a player input to the KMessageServer.
Definition: kgame.cpp:721
bool addProperty(KGamePropertyBase *data, const QString &name=QString())
Adds a KGameProperty property to the handler.
virtual void close() override
void signalPropertyChanged(KGamePropertyBase *property, KGame *me)
This signal is emitted if a player property changes its value and the property is set to notify this ...
uint minPlayers() const
What is the minimal number of players?
Definition: kgame.cpp:685
void setGameStatus(int status)
sets the game status
Definition: kgame.cpp:860
bool addProperty(KGamePropertyBase *data)
docu: see KPlayer
Definition: kgame.cpp:1474
KGamePlayerList * playerList()
Returns a list of all active players.
Definition: kgame.cpp:710
KGameSequence * gameSequence() const
Definition: kgame.cpp:833
void networkTransmission(QDataStream &stream, int msgid, quint32 sender)
Receives a message.
Definition: kplayer.cpp:397
void slotClientConnected(quint32 clientId)
Calls negotiateNetworkGame() See KGameNetwork::signalClientConnected.
Definition: kgame.cpp:1295
void disconnect()
Disconnect the current connection and establish a new local one.
quint32 gameId() const
The unique ID of this game.
void Debug() override
Gives debug output of the game status.
Definition: kgame.cpp:1282
void signalClientJoinedGame(quint32 clientid, KGame *me)
Is emitted after a client is successfully connected to the game.
void setGameSequence(KGameSequence *sequence)
Set a new KGameSequence to control player management.
Definition: kgame.cpp:823
virtual bool save(QDataStream &stream, bool saveplayers=true)
Save a game to a file OR to network.
Definition: kgame.cpp:286
bool isActive() const
Is this player an active player.
Definition: kplayer.cpp:143
QIODevice * device() const const
virtual void Debug()
Gives debug output of the game status.
virtual int rtti() const
The identification of the player.
Definition: kplayer.cpp:113
int maxPlayers() const
What is the maximal number of players?
Definition: kgame.cpp:688
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool sendSystemMessage(const QByteArray &buffer, int msgid, quint32 receiver=0, quint32 sender=0)
Sends a network message msg with a given msg id msgid to all clients.
KGamePropertyBase * findProperty(int id) const
This function allows to find the pointer to a player property when you know its id.
Definition: kgame.cpp:1494
void playerDeleted(KPlayer *player)
Called by the destructor of KPlayer to remove itself from the game.
Definition: kgame.cpp:499
void signalClientDisconnected(quint32 clientID, bool broken)
This signal is emitted whenever the KMessageServer sends us a message that a connection to a client w...
QList::iterator begin()
void unlockDirectEmit()
Removes the lock from the emitting of property signals.
void setPolicy(GamePolicy p, bool recursive=true)
Changes the consistency policy of a property.
Definition: kgame.cpp:1501
Q_EMITQ_EMIT
void setMaster()
uint playerCount() const
Returns how many players are plugged into the game.
Definition: kgame.cpp:691
void signalMessageUpdate(int msgid, quint32 receiver, quint32 sender)
We got an network message.
void sendProperty(int msgid, QDataStream &stream, bool *sent)
Called by KGamePropertyHandler only! Internal function!
Definition: kgame.cpp:1480
virtual KPlayer * createPlayer(int rtti, int io, bool isvirtual)
This virtual function is called if the KGame needs to create a new player.
Definition: kgame.cpp:353
virtual KPlayer * nextPlayer(KPlayer *last, bool exclusive=true)
Definition: kgame.cpp:850
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Dec 7 2021 22:34:14 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.