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 <b_mann@gmx.de>
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
35class KGamePrivate
36{
37public:
38 KGamePrivate() = default;
39
40public:
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 --------------------------
60KGame::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
107void 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
124void KGame::deleteInactivePlayers()
125{
126 qDeleteAll(d->mInactivePlayerList);
127 d->mInactivePlayerList.clear();
128}
129
130bool 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
145bool KGame::load(QDataStream &stream, bool reset)
146{
147 return loadgame(stream, false, reset);
148}
149
150bool 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
234bool 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
249bool KGame::save(QDataStream &stream, bool saveplayers)
250{
251 return savegame(stream, false, saveplayers);
252}
253
254bool 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
308KPlayer *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}
313KPlayer *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
343KPlayer *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...
365bool 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() << ")";
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
450bool 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
470void 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
485bool 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
588void KGame::setMaxPlayers(uint maxnumber)
589{
590 if (isAdmin()) {
591 d->mMaxPlayer.changeValue(maxnumber);
592 }
593}
594
595void KGame::setMinPlayers(uint minnumber)
596{
597 if (isAdmin()) {
598 d->mMinPlayer.changeValue(minnumber);
599 }
600}
601
603{
604 return d->mMinPlayer.value();
605}
606
608{
609 return d->mMaxPlayer.value();
610}
611
613{
614 return d->mPlayerList.count();
615}
616
618{
619 return d->mGameStatus.value();
620}
621
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
652bool 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
668bool 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
744void 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();
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
899void 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
1027void 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
1076void KGame::slotClientConnected(quint32 clientID)
1077{
1078 if (isAdmin()) {
1079 negotiateNetworkGame(clientID);
1080 }
1081}
1082
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
1142void 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)
1190void 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
1213bool 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
1227bool 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
1232bool 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
1245bool KGame::sendPlayerProperty(int msgid, QDataStream &s, quint32 playerId)
1246{
1247 return sendSystemMessage(s, msgid, playerId);
1248}
1249
1250void KGame::sendProperty(int msgid, QDataStream &stream, bool *sent)
1251{
1252 bool s = sendSystemMessage(stream, msgid);
1253 if (s) {
1254 *sent = true;
1255 }
1256}
1257
1262
1264{
1265 return d->mProperties->find(id);
1266}
1267
1269{
1270 return d->mPolicy;
1271}
1272void 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 */
static bool isPlayer(quint32 id)
Checks whether a message receiver/sender is a player.
static int version()
static quint32 rawGameId(quint32 playerid)
Returns the raw game id, that is, the game id the player belongs to.
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...
The KGameNetwork class is the KGame class with network support.
bool isAdmin() const
The admin of a game is the one who initializes newly connected clients using negotiateNetworkGame and...
void signalClientConnected(quint32 clientID)
This signal is emitted whenever the KMessageServer sends us a message that a new client connected.
virtual void Debug()
Gives debug output of the game status.
void disconnect()
Disconnect the current connection and establish a new local one.
int cookie() const
Application cookie.
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.
quint32 gameId() const
The unique ID of this game.
void signalConnectionBroken()
Our connection to the KMessageServer has broken.
void signalClientDisconnected(quint32 clientID, bool broken)
This signal is emitted whenever the KMessageServer sends us a message that a connection to a client w...
void sendError(int error, const QByteArray &message, quint32 receiver=0, quint32 sender=0)
Sends a network message.
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.
Base class of KGameProperty.
PropertyPolicy
The policy of the property.
int registerData(int id, KGamePropertyHandler *owner, PropertyPolicy p, const QString &name=QString())
You have to register a KGamePropertyBase before you can use it.
A collection class for KGameProperty objects.
void setPolicy(KGamePropertyBase::PropertyPolicy p, bool userspace=true)
Set the policy for all kgame variables which are currently registered in the KGame property handler.
void registerHandler(int id, const QObject *receiver, const char *send, const char *emit)
Register the handler with a parent.
virtual bool save(QDataStream &stream)
Saves properties into the datastream.
bool addProperty(KGamePropertyBase *data, const QString &name=QString())
Adds a KGameProperty property to the handler.
virtual bool load(QDataStream &stream)
Loads properties from the datastream.
void unlockDirectEmit()
Removes the lock from the emitting of property signals.
KGamePropertyBase * find(int id)
void lockDirectEmit()
Called by the KGame or KPlayer object or the handler itself to delay emitting of signals.
bool processMessage(QDataStream &stream, int id, bool isSender)
Main message process function.
const type & value() const
void changeValue(type v)
This function does both, change the local value and change the network value.
bool setLocal(type v)
This function sets the value of the property directly, i.e.
This class takes care of round or move management as well of the gameover condition.
void setGame(KGame *game)
Set the KGame object for this sequence.
virtual KPlayer * nextPlayer(KPlayer *last, bool exclusive=true)
Select the next player in a turn based game.
virtual int checkGameOver(KPlayer *player)
Check whether the game is over.
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 signalSavePrePlayers(QDataStream &stream)
The game will be saved to the given stream.
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
KGamePlayerList * inactivePlayerList()
Returns a list of all inactive players.
Definition kgame.cpp:632
virtual void newPlayersJoin(KGamePlayerList *oldplayer, KGamePlayerList *newplayer, QList< int > &inactivate)
This virtual function can be overwritten for your own player management.
Definition kgame.h:712
~KGame() override
Destructs the game.
Definition kgame.cpp:90
bool sendPlayerProperty(int msgid, QDataStream &s, quint32 playerId)
This is called by KPlayer::sendProperty only! Internal function!
Definition kgame.cpp:1245
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
int maxPlayers() const
What is the maximal number of players?
Definition kgame.cpp:607
void signalMessageUpdate(int msgid, quint32 receiver, quint32 sender)
We got an network message.
bool removePlayer(KPlayer *player)
Sends a message over the network, msgid=IdRemovePlayer.
Definition kgame.h:197
void signalSave(QDataStream &stream)
The game will be saved to the given stream.
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 ...
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
void signalReplacePlayerIO(KPlayer *player, bool *remove)
When a client disconnects from the game usually all players from that client are removed.
void setGameSequence(KGameSequence *sequence)
Set a new KGameSequence to control player management.
Definition kgame.cpp:720
virtual bool savegame(QDataStream &stream, bool network, bool saveplayers)
Save a game, to file OR network.
Definition kgame.cpp:254
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 signalNetworkData(int msgid, const QByteArray &buffer, quint32 receiver, quint32 sender)
We got an user defined update message.
bool systemAddPlayer(KPlayer *newplayer)
Adds a player to the game.
Definition kgame.cpp:407
void slotServerDisconnected()
This slot is called whenever the connection to the server is lost (ie the signal KGameNetwork::signal...
Definition kgame.cpp:1083
bool activatePlayer(KPlayer *player)
sends activate player: internal use only?
Definition kgame.cpp:552
void signalGameOver(int status, KPlayer *current, KGame *me)
Is emitted after a call to gameOver() returns a non zero return code.
void signalPlayerJoinedGame(KPlayer *player)
a player joined the game
bool isRunning() const
Is the game running.
Definition kgame.cpp:622
virtual bool reset()
Resets the game, i.e.
Definition kgame.cpp:100
void Debug() override
Gives debug output of the game status.
Definition kgame.cpp:1063
KPlayer * findPlayer(quint32 id) const
Returns the player object for a given player id.
Definition kgame.cpp:343
bool systemInactivatePlayer(KPlayer *player)
inactivates player.
Definition kgame.cpp:528
bool systemActivatePlayer(KPlayer *player)
activates player.
Definition kgame.cpp:568
virtual bool loadgame(QDataStream &stream, bool network, bool reset)
Load a saved game, from file OR network.
Definition kgame.cpp:150
KPlayer * playerInputFinished(KPlayer *player)
Called after the player input is processed by the game.
Definition kgame.cpp:692
KGamePropertyHandler * dataHandler() const
Returns a pointer to the KGame property handler.
Definition kgame.cpp:627
void signalLoadError(QDataStream &stream, bool network, int cookie, bool &result)
Is emitted if a game with a different version cookie is loaded.
KGamePlayerList * playerList()
Returns a list of all active players.
Definition kgame.cpp:642
void emitSignal(KGamePropertyBase *me)
Called by KGamePropertyHandler only! Internal function!
Definition kgame.cpp:1258
void sendProperty(int msgid, QDataStream &stream, bool *sent)
Called by KGamePropertyHandler only! Internal function!
Definition kgame.cpp:1250
int gameStatus() const
returns the game status, ie running,pause,ended,...
Definition kgame.cpp:617
KPlayer * loadPlayer(QDataStream &stream, bool isvirtual=false)
Load the player list from a stream.
Definition kgame.cpp:313
KGameSequence * gameSequence() const
Definition kgame.cpp:729
void setMinPlayers(uint minnumber)
Set the minimal number of players.
Definition kgame.cpp:595
bool addProperty(KGamePropertyBase *data)
docu: see KPlayer
Definition kgame.cpp:1240
void setGameStatus(int status)
sets the game status
Definition kgame.cpp:734
virtual bool load(QDataStream &stream, bool reset=true)
Load a saved game, from file OR network.
Definition kgame.cpp:145
uint playerCount() const
Returns how many players are plugged into the game.
Definition kgame.cpp:612
GamePolicy
The policy of the property.
Definition kgame.h:74
bool inactivatePlayer(KPlayer *player)
sends inactivate player: internal use only?
Definition kgame.cpp:510
KGame(int cookie=42, QObject *parent=nullptr)
Create a KGame object.
Definition kgame.cpp:60
void playerDeleted(KPlayer *player)
Called by the destructor of KPlayer to remove itself from the game.
Definition kgame.cpp:435
virtual void negotiateNetworkGame(quint32 clientID)
This member function will transmit e.g.
Definition kgame.cpp:1190
void signalPlayerLeftGame(KPlayer *player)
a player left the game because of a broken connection or so!
virtual bool systemPlayerInput(QDataStream &msg, KPlayer *player, quint32 sender=0)
Called when a player input arrives from KMessageServer.
Definition kgame.cpp:668
void savePlayers(QDataStream &stream, KGamePlayerList *list=nullptr)
Save the player list to a stream.
Definition kgame.cpp:293
void setMaxPlayers(uint maxnumber)
Set the maximal number of players.
Definition kgame.cpp:588
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...
void savePlayer(QDataStream &stream, KPlayer *player)
Prepare a player for being added.
Definition kgame.cpp:284
bool sendGroupMessage(const QByteArray &msg, int msgid, quint32 sender, const QString &group)
See KGameNetwork::sendMessage.
Definition kgame.cpp:1213
GamePolicy policy() const
Definition kgame.cpp:1268
void signalLoadPrePlayers(QDataStream &stream)
The game will be loaded from the given stream.
void signalLoad(QDataStream &stream)
The game will be loaded from the given stream.
void signalClientJoinedGame(quint32 clientid, KGame *me)
Is emitted after a client is successfully connected to the game.
uint minPlayers() const
What is the minimal number of players?
Definition kgame.cpp:602
virtual bool playerInput(QDataStream &msg, KPlayer *player)=0
A player input occurred.
virtual bool save(QDataStream &stream, bool saveplayers=true)
Save a game to a file OR to network.
Definition kgame.cpp:249
void slotClientConnected(quint32 clientId)
Calls negotiateNetworkGame() See KGameNetwork::signalClientConnected.
Definition kgame.cpp:1076
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 setPolicy(GamePolicy p, bool recursive=true)
Changes the consistency policy of a property.
Definition kgame.cpp:1272
void systemRemovePlayer(KPlayer *player, bool deleteit)
Removes a player from the game.
Definition kgame.cpp:470
Base class for a game player.
Definition kplayer.h:60
void setActive(bool v)
Set an player as active (true) or inactive (false)
Definition kplayer.cpp:140
void setVirtual(bool v)
Definition kplayer.cpp:237
int calcIOValue()
Calculates a checksum over the IO devices.
Definition kplayer.cpp:319
bool isVirtual() const
Is this player a virtual player, i.e.
Definition kplayer.cpp:242
virtual int rtti() const
The identification of the player.
Definition kplayer.cpp:105
void networkTransmission(QDataStream &stream, int msgid, quint32 sender)
Receives a message.
Definition kplayer.cpp:387
virtual bool save(QDataStream &stream)
Save a player to a file OR to network.
Definition kplayer.cpp:375
bool asyncInput() const
Query whether this player does asynchronous input.
Definition kplayer.cpp:130
quint32 id() const
Returns the id of the player.
Definition kplayer.cpp:227
bool setTurn(bool b, bool exclusive=true)
Sets whether this player is the next to turn.
Definition kplayer.cpp:329
bool isActive() const
Is this player an active player.
Definition kplayer.cpp:135
virtual bool load(QDataStream &stream)
Load a saved player, from file OR network.
Definition kplayer.cpp:352
virtual const QString & group() const
Query the group the player belongs to.
Definition kplayer.cpp:212
void setGame(KGame *game)
sets the game the player belongs to.
Definition kplayer.cpp:115
int networkPriority() const
Returns whether this player can be replaced by a network connection player.
Definition kplayer.cpp:257
void Debug()
Gives debug output of the game status.
Definition kplayer.cpp:448
Q_SCRIPTABLE Q_NOREPLY void abort()
Q_SCRIPTABLE CaptureState status()
QString i18n(const char *text, const TYPE &arg...)
QIODevice * device() const const
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
virtual void close() override
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
iterator begin()
void clear()
qsizetype count() const const
iterator end()
qsizetype indexOf(const AT &value, qsizetype from) const const
bool isEmpty() const const
void prepend(parameter_type value)
qsizetype removeAll(const AT &t)
value_type takeFirst()
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * sender() const const
T dequeue()
void enqueue(const T &t)
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:13:43 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.