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

KDE's Doxygen guidelines are available online.