KDEGames

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

KDE's Doxygen guidelines are available online.