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

KDE's Doxygen guidelines are available online.