/* This file is part of the TDE games library Copyright (C) 2001 Martin Heni (martin@heni-online.de) Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* $Id$ */ #include "kgame.h" #include "kgame.moc" #include "kgamepropertyhandler.h" #include "kgameproperty.h" #include "kplayer.h" #include "kgameio.h" #include "kgameerror.h" #include "kgamesequence.h" #include "kgamemessage.h" #include #include #include #include #include #include #include #include #include #include #define KGAME_LOAD_COOKIE 4210 // try to place as much as possible here // many things are *not* possible here as KGame has to use some inline function class KGamePrivate { public: KGamePrivate() { mUniquePlayerNumber = 0; mPolicy=KGame::PolicyLocal; mGameSequence = 0; } int mUniquePlayerNumber; TQPtrQueue mAddPlayerList;// this is a list of to-be-added players. See addPlayer() docu KRandomSequence* mRandom; KGame::GamePolicy mPolicy; KGameSequence* mGameSequence; KGamePropertyHandler* mProperties; // player lists KGame::KGamePlayerList mPlayerList; KGame::KGamePlayerList mInactivePlayerList; //KGamePropertys KGamePropertyInt mMaxPlayer; KGamePropertyUInt mMinPlayer; KGamePropertyInt mGameStatus; // Game running? TQValueList mInactiveIdList; }; // ------------------- GAME CLASS -------------------------- KGame::KGame(int cookie,TQObject* parent) : KGameNetwork(cookie,parent) { kdDebug(11001) << k_funcinfo << " - " << this << ", sizeof(KGame)=" << sizeof(KGame) << endl; d = new KGamePrivate; d->mProperties = new KGamePropertyHandler(this); d->mProperties->registerHandler(KGameMessage::IdGameProperty, this,TQT_SLOT(sendProperty(int, TQDataStream&, bool* )), TQT_SLOT(emitSignal(KGamePropertyBase *))); d->mMaxPlayer.registerData(KGamePropertyBase::IdMaxPlayer, this, i18n("MaxPlayers")); d->mMaxPlayer.setLocal(-1); // Infinite d->mMinPlayer.registerData(KGamePropertyBase::IdMinPlayer, this, i18n("MinPlayers")); d->mMinPlayer.setLocal(0); // Always ok d->mGameStatus.registerData(KGamePropertyBase::IdGameStatus, this, i18n("GameStatus")); d->mGameStatus.setLocal(Init); // d->mUniquePlayerNumber = 0; d->mRandom = new KRandomSequence; d->mRandom->setSeed(0); connect(this, TQT_SIGNAL(signalClientConnected(TQ_UINT32)), this, TQT_SLOT(slotClientConnected(TQ_UINT32))); connect(this, TQT_SIGNAL(signalClientDisconnected(TQ_UINT32,bool)), this, TQT_SLOT(slotClientDisconnected(TQ_UINT32,bool))); connect(this, TQT_SIGNAL(signalConnectionBroken()), this, TQT_SLOT(slotServerDisconnected())); setGameSequence(new KGameSequence()); // BL: FIXME This signal does no longer exist. When we are merging // MH: super....and how do I find out about the lost conenction now? // KGame and KGameNetwork, this could be improved! // connect(this,TQT_SIGNAL(signalConnectionLost(KGameClient *)), // this,TQT_SLOT(slotConnectionLost(KGameClient *))); } KGame::~KGame() { kdDebug(11001) << k_funcinfo << endl; // Debug(); reset(); delete d->mGameSequence; delete d->mRandom; delete d; kdDebug(11001) << k_funcinfo << " done" << endl; } bool KGame::reset() { deletePlayers(); deleteInactivePlayers(); return true; } void KGame::deletePlayers() { // kdDebug(11001) << k_funcinfo << endl; KGamePlayerList tmp = d->mPlayerList; // in case of PolicyClean player=d->mPlayerList.first() is infinite KPlayer *player; while((player=tmp.first())) { delete player; // delete and removes the player tmp.removeFirst(); } // kdDebug(11001) << k_funcinfo << " done" << endl; } void KGame::deleteInactivePlayers() { KPlayer *player; while((player=d->mInactivePlayerList.first())) { //player->setGame(0); // prevent call backs d->mInactivePlayerList.remove(player); delete player; } } bool KGame::load(TQString filename,bool reset) { if (filename.isNull()) { return false; } TQFile f(filename); if (!f.open(IO_ReadOnly)) { return false; } TQDataStream s( &f ); load(s,reset); f.close(); return true; } bool KGame::load(TQDataStream &stream,bool reset) { return loadgame(stream, false,reset); } bool KGame::loadgame(TQDataStream &stream, bool network,bool resetgame) { // Load Game Data // internal data TQ_INT32 c; stream >> c; // cookie if (c!=cookie()) { kdWarning(11001) << "Trying to load different game version we="<id() << " to indirect emit" <load(stream); // If there is additional data to be loaded before players are loaded then do // this here. emit signalLoadPrePlayers(stream); // Load Playerobjects uint playercount; stream >> playercount; kdDebug(11001) << "Loading KGame " << playercount << " KPlayer objects " << endl; for (i=0;i> cookie; if (cookie==KGAME_LOAD_COOKIE) { kdDebug(11001) << " Game loaded propertly"<unlockDirectEmit(); for ( player=playerList()->first(); player != 0; player=playerList()->next() ) { player->dataHandler()->unlockDirectEmit(); // kdDebug(11001) << "Player "<id() << " to direct emit" <mUniquePlayerNumber; int newseed=(int)d->mRandom->getLong(65535); stream << newseed; d->mRandom->setSeed(newseed); // Properties dataHandler()->save(stream); // Save all data that need to be saved *before* the players are saved emit signalSavePrePlayers(stream); if (saveplayers) { savePlayers(stream,playerList()); } else { stream << (uint)0; // no players saved } stream << (TQ_INT16)KGAME_LOAD_COOKIE; emit signalSave(stream); return true; } void KGame::savePlayer(TQDataStream &stream,KPlayer* p) { // this could be in KGameMessage as well stream << (TQ_INT32)p->rtti(); stream << (TQ_INT32)p->id(); stream << (TQ_INT32)p->calcIOValue(); p->save(stream); } void KGame::savePlayers(TQDataStream &stream, KGamePlayerList *list) { if (!list) { list=playerList(); } TQ_INT32 cnt=list->count(); kdDebug(11001) << "Saving KGame " << cnt << " KPlayer objects " << endl; stream << cnt; KPlayer *player; for ( player=list->first(); player != 0; player=list->next() ) { savePlayer(stream,player); } } KPlayer *KGame::createPlayer(int /*rtti*/,int /*io*/,bool /*isvirtual*/) { kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl; return new KPlayer; } KPlayer *KGame::loadPlayer(TQDataStream& stream,bool isvirtual) { TQ_INT32 rtti,id,iovalue; stream >> rtti >> id >> iovalue; KPlayer *newplayer=findPlayer(id); if (!newplayer) { kdDebug(11001) << k_funcinfo << "Player "<< id << " not found...asking user to create one " << endl; newplayer=createPlayer(rtti,iovalue,isvirtual); //emit signalCreatePlayer(newplayer,rtti,iovalue,isvirtual,this); } /* if (!newplayer) { kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl; newplayer=new KPlayer; } else { kdDebug(11001) << " USER Player " << newplayer << " done player->rtti=" << newplayer->rtti() << " rtti=" << rtti << endl; } */ newplayer->load(stream); if (isvirtual) { newplayer->setVirtual(true); } return newplayer; } // ----------------- Player handling ----------------------- KPlayer * KGame::findPlayer(TQ_UINT32 id) const { for (TQPtrListIterator it(d->mPlayerList); it.current(); ++it) { if (it.current()->id() == id) { return it.current(); } } for (TQPtrListIterator it(d->mInactivePlayerList); it.current(); ++it) { if (it.current()->id() == id) { return it.current(); } } return 0; } // it is necessary that addPlayer and systemAddPlayer are called in the same // order. Ie if addPlayer(foo) followed by addPlayer(bar) is called, you must // not call systemAddPlayer(bar) followed by systemAddPlayer(foo), as the // mAddPlayerList would get confused. Should be no problem as long as comServer // and the clients are working correctly. // BUT: if addPlayer(foo) does not arrive by any reason while addPlayer(bar) // does, we would be in trouble... void KGame::addPlayer(KPlayer* newplayer) { kdDebug(11001) << k_funcinfo << ": " << "; maxPlayers=" << maxPlayers() << " playerCount=" << playerCount() << endl; if (!newplayer) { kdFatal(11001) << "trying to add NULL player in KGame::addPlayer()" << endl; return ; } if (maxPlayers() >= 0 && (int)playerCount() >= maxPlayers()) { kdWarning(11001) << "cannot add more than " << maxPlayers() << " players - deleting..." << endl; delete newplayer; return; } if (newplayer->id() == 0) { d->mUniquePlayerNumber++; newplayer->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber, gameId())); kdDebug(11001) << k_funcinfo << "NEW!!! player " << newplayer << " now has id " << newplayer->id() << endl; } else { // this could happen in games which use their own ID management by certain // reasons. that is NOT recommended kdDebug(11001) << k_funcinfo << "player " << newplayer << " already has an id: " << newplayer->id() << endl; } TQByteArray buffer; TQDataStream stream(buffer,IO_WriteOnly); // We distinguis here what policy we have if (policy()==PolicyLocal || policy()==PolicyDirty) { systemAddPlayer(newplayer); } if (policy()==PolicyClean || policy()==PolicyDirty) { savePlayer(stream,newplayer); // Store the player for delayed clean adding if (policy()==PolicyClean) { d->mAddPlayerList.enqueue(newplayer); } sendSystemMessage(stream,(int)KGameMessage::IdAddPlayer, 0); } } void KGame::systemAddPlayer(KPlayer* newplayer) { if (!newplayer) { kdFatal(11001) << "trying to add NULL player in KGame::systemAddPlayer()" << endl; return ; } if (newplayer->id() == 0) { kdWarning(11001) << k_funcinfo << "player " << newplayer << " has no ID" << endl; } if (findPlayer(newplayer->id())) { kdError(11001) << "ERROR: Double adding player !!!!! NOT GOOD !!!!!! " << newplayer->id() << "...I delete it again" << endl; delete newplayer; } else { kdDebug(11001) << "Trying to add player " << newplayer <<" maxPlayers="<id() << ") to be removed " << player << endl; if (policy()==PolicyLocal || policy()==PolicyDirty) { systemRemovePlayer(player,false); } if (policy()==PolicyClean || policy()==PolicyDirty) { if (!player->isVirtual()) { kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<id() << endl; sendSystemMessage(player->id(), KGameMessage::IdRemovePlayer, 0); } } } bool KGame::removePlayer(KPlayer * player, TQ_UINT32 receiver) {//transmit to all clients, or to receiver only if (!player) { kdFatal(11001) << "trying to remove NULL player in KGame::removePlayer()" << endl; return false; } kdDebug(11001) << k_funcinfo << ": id (" << player->id() << ") to be removed " << player << endl; if (policy()==PolicyLocal || policy()==PolicyDirty) { systemRemovePlayer(player,true); } if (policy()==PolicyClean || policy()==PolicyDirty) { kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<id() << endl; sendSystemMessage(player->id(),KGameMessage::IdRemovePlayer, receiver); } return true; // we will receive the message in networkTransmission() } void KGame::systemRemovePlayer(KPlayer* player,bool deleteit) { kdDebug(11001) << k_funcinfo << endl; if (!player) { kdWarning(11001) << "cannot remove NULL player" << endl; return; } if (!systemRemove(player,deleteit)) { kdWarning(11001) << "player " << player << "(" << player->id() << ") Could not be found!" << endl; } if (gameStatus()==(int)Run && playerCount()id() << ") to be removed " << p << endl; if (d->mPlayerList.count() == 0) { result = false; } else { result = d->mPlayerList.remove(p); } emit signalPlayerLeftGame(p); p->setGame(0); if (deleteit) { delete p; } return result; } bool KGame::inactivatePlayer(KPlayer* player) { if (!player) { return false; } kdDebug(11001) << "Inactivate player " << player->id() << endl; if (policy()==PolicyLocal || policy()==PolicyDirty) { systemInactivatePlayer(player); } if (policy()==PolicyClean || policy()==PolicyDirty) { sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer); } return true; } bool KGame::systemInactivatePlayer(KPlayer* player) { if (!player || !player->isActive()) { return false; } kdDebug(11001) << " Inactivate player " << player->id() << endl; int pid=player->id(); // Virtual players cannot be deactivated. They will be removed if (player->isVirtual()) { systemRemovePlayer(player,true); } else { d->mPlayerList.remove(player); d->mInactivePlayerList.prepend(player); player->setActive(false); } emit signalPlayerLeftGame(player); if (isAdmin()) { d->mInactiveIdList.prepend(pid); } return true; } bool KGame::activatePlayer(KPlayer * player) { if (!player) { return false; } kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl; if (policy()==PolicyLocal || policy()==PolicyDirty) { systemActivatePlayer(player); } if (policy()==PolicyClean || policy()==PolicyDirty) { sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer); } return true; } bool KGame::systemActivatePlayer(KPlayer* player) { if (!player || player->isActive()) { return false; } kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl; d->mInactivePlayerList.remove(player); player->setActive(true); addPlayer(player); if (isAdmin()) { d->mInactiveIdList.remove(player->id()); } return true; } // -------------------- Properties --------------------------- void KGame::setMaxPlayers(uint maxnumber) { if (isAdmin()) { d->mMaxPlayer.changeValue(maxnumber); } } void KGame::setMinPlayers(uint minnumber) { if (isAdmin()) { d->mMinPlayer.changeValue(minnumber); } } uint KGame::minPlayers() const { return d->mMinPlayer.value(); } int KGame::maxPlayers() const { return d->mMaxPlayer.value(); } uint KGame::playerCount() const { return d->mPlayerList.count(); } int KGame::gameStatus() const { return d->mGameStatus.value(); } bool KGame::isRunning() const { return d->mGameStatus.value() == Run; } KGamePropertyHandler* KGame::dataHandler() const { return d->mProperties; } KGame::KGamePlayerList* KGame::inactivePlayerList() { return &d->mInactivePlayerList; } const KGame::KGamePlayerList* KGame::inactivePlayerList() const { return &d->mInactivePlayerList; } KGame::KGamePlayerList* KGame::playerList() { return &d->mPlayerList; } const KGame::KGamePlayerList* KGame::playerList() const { return &d->mPlayerList; } KRandomSequence* KGame::random() const { return d->mRandom; } bool KGame::sendPlayerInput(TQDataStream &msg, KPlayer *player, TQ_UINT32 sender) { if (!player) { kdError(11001) << k_funcinfo << ": NULL player" << endl; return false; } if (!isRunning()) { kdError(11001) << k_funcinfo << ": game not running" << endl; return false; } kdDebug(11001) << k_funcinfo << ": transmitting playerInput over network" << endl; sendSystemMessage(msg, (int)KGameMessage::IdPlayerInput, player->id(), sender); return true; } bool KGame::systemPlayerInput(TQDataStream &msg, KPlayer *player, TQ_UINT32 sender) { if (!player) { kdError(11001) << k_funcinfo << ": NULL player" << endl; return false; } if (!isRunning()) { kdError(11001) << k_funcinfo << ": game not running" << endl; return false; } kdDebug(11001) << "KGame: Got playerInput from messageServer... sender: " << sender << endl; if (playerInput(msg,player)) { playerInputFinished(player); } else { kdDebug(11001) << k_funcinfo<<": switching off player input"<asyncInput()) { player->setTurn(false); // in turn based games we have to switch off input now } } return true; } KPlayer * KGame::playerInputFinished(KPlayer *player) { kdDebug(11001) << k_funcinfo<<"player input finished for "<id()<setCurrentPlayer(player); } // do not call gameSequence()->checkGameOver() to keep backward compatibility! gameOver = checkGameOver(player); if (gameOver!=0) { if (player) { player->setTurn(false); } setGameStatus(End); emit signalGameOver(gameOver,player,this); } else if (!player->asyncInput()) { player->setTurn(false); // in turn based games we have to switch off input now if (gameSequence()) { TQTimer::singleShot(0,this,TQT_SLOT(prepareNext())); } } return player; } // Per default we do not do anything int KGame::checkGameOver(KPlayer *player) { if (gameSequence()) { return gameSequence()->checkGameOver(player); } return 0; } void KGame::setGameSequence(KGameSequence* sequence) { delete d->mGameSequence; d->mGameSequence = sequence; if (d->mGameSequence) { d->mGameSequence->setGame(this); } } KGameSequence* KGame::gameSequence() const { return d->mGameSequence; } void KGame::prepareNext() { if (gameSequence()) { // we don't call gameSequence->nextPlayer() to keep old code working nextPlayer(gameSequence()->currentPlayer()); } } KPlayer *KGame::nextPlayer(KPlayer *last,bool exclusive) { if (gameSequence()) { return gameSequence()->nextPlayer(last, exclusive); } return 0; } void KGame::setGameStatus(int status) { kdDebug(11001) << k_funcinfo << ": GAMESTATUS CHANGED to" << status << endl; if (status==(int)Run && playerCount()mGameStatus = status; } void KGame::networkTransmission(TQDataStream &stream, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender, TQ_UINT32 /*clientID*/) {//clientID is unused // message targets a playerobject. If we find it we forward the message to the // player. Otherwise we proceed here and hope the best that the user processes // the message // kdDebug(11001) << k_funcinfo << ": we="<<(int)gameId()<<" id="<isActive()<<" recv="<< receiver << endl; KPlayer *p=findPlayer(receiver); if (p && p->isActive()) { p->networkTransmission(stream,msgid,sender); return; } if (p) { kdDebug(11001) << "player is here but not active" << endl; } else { kdDebug(11001) << "no player found" << endl; } } // If it is not for a player it is meant for us!!!! Otherwise the // gamenetwork would not have passed the message to us! // GameProperties processed if (d->mProperties->processMessage(stream, msgid, sender == gameId())) { // kdDebug(11001 ) << "KGame: message taken by property - returning" << endl; return ; } switch(msgid) { case KGameMessage::IdSetupGame: // Client: First step in setup game { TQ_INT16 v; TQ_INT32 c; stream >> v >> c; kdDebug(11001) << " ===================> (Client) " << k_funcinfo << ": Got IdSetupGame ================== " << endl; kdDebug(11001) << "our game id is " << gameId() << " Lib version=" << v << " App Cookie=" << c << endl; // Verify identity of the network partners if (c!=cookie()) { kdError(11001) << "IdGameSetup: Negotiate Game: cookie mismatch I'am="<(Master) " << k_funcinfo << " - IdSetupGameContinue" << endl; setupGameContinue(stream, sender); } break; case KGameMessage::IdActivatePlayer: // Activate Player { int id; stream >> id; kdDebug(11001) << "Got IdActivatePlayer id=" << id << endl; if (sender!=gameId() || policy()!=PolicyDirty) { systemActivatePlayer(findPlayer(id)); } } break; case KGameMessage::IdInactivatePlayer: // Inactivate Player { int id; stream >> id; kdDebug(11001) << "Got IdInactivatePlayer id=" << id << endl; if (sender!=gameId() || policy()!=PolicyDirty) { systemInactivatePlayer(findPlayer(id)); } } break; case KGameMessage::IdAddPlayer: { kdDebug(11001) << k_funcinfo << ": Got IdAddPlayer" << endl; if (sender!=gameId() || policy()!=PolicyDirty) { KPlayer *newplayer=0; // We sent the message so the player is already available if (sender==gameId()) { kdDebug(11001) << "dequeue previously added player" << endl; newplayer = d->mAddPlayerList.dequeue(); } else { newplayer=loadPlayer(stream,true); } systemAddPlayer(newplayer);// the final, local, adding //systemAddPlayer(stream); } } break; case KGameMessage::IdRemovePlayer: // Client should delete player id { int id; stream >> id; kdDebug(11001) << k_funcinfo << ": Got IdRemovePlayer " << id << endl; KPlayer *p=findPlayer(id); if (p) { // Otherwise the player is already removed if (sender!=gameId() || policy()!=PolicyDirty) { systemRemovePlayer(p,true); } } else { kdWarning(11001) << k_funcinfo << "Cannot find player " << id << endl; } } break; case KGameMessage::IdGameLoad: { kdDebug(11001) << "====> (Client) " << k_funcinfo << ": Got IdGameLoad" << endl; loadgame(stream,true,false); } break; case KGameMessage::IdGameSetupDone: { int cid; stream >> cid; kdDebug(11001) << "====> (CLIENT) " << k_funcinfo << ": Got IdGameSetupDone for client " << cid << " we are =" << gameId() << endl; sendSystemMessage(gameId(), KGameMessage::IdGameConnected, 0); } break; case KGameMessage::IdGameConnected: { int cid; stream >> cid; kdDebug(11001) << "====> (ALL) " << k_funcinfo << ": Got IdGameConnected for client "<< cid << " we are =" << gameId() << endl; emit signalClientJoinedGame(cid,this); } break; case KGameMessage::IdSyncRandom: // Master forces a new random seed on us { int newseed; stream >> newseed; kdDebug(11001) << "CLIENT: setting random seed to " << newseed << endl; d->mRandom->setSeed(newseed); } break; case KGameMessage::IdDisconnect: { // if we disconnect we *always* start a local game. // this could lead into problems if we just change the message server if (sender != gameId()) { kdDebug(11001) << "client " << sender << " leaves game" << endl; return; } kdDebug(11001) << "leaving the game" << endl; // start a new local game // no other client is by default connected to this so this call should be // enough setMaster(); } break; default: { if (msgid < KGameMessage::IdUser) { kdError(11001) << "incorrect message id " << msgid << " - emit anyway" << endl; } kdDebug(11001) << k_funcinfo << ": User data msgid " << msgid << endl; emit signalNetworkData(msgid - KGameMessage::IdUser,((TQBuffer*)stream.device())->readAll(),receiver,sender); } break; } } // called by the IdSetupGameContinue Message - MASTER SIDE // Here the master needs to decide which players can take part at the game // and which will be deactivated void KGame::setupGameContinue(TQDataStream& stream, TQ_UINT32 sender) { KPlayer *player; TQ_INT32 cnt; int i; stream >> cnt; TQValueList inactivateIds; KGamePlayerList newPlayerList; newPlayerList.setAutoDelete(true); for (i=0;iid() <<" rawgame=" << KGameMessage::rawGameId(player->id()) << " from sender " << sender << endl; if (KGameMessage::rawGameId(player->id()) != sender) { kdError(11001) << "Client tries to add player with wrong game id - cheat possible" << endl; } else { newPlayerList.append(player); kdDebug(11001) << " newplayerlist appended " << player->id() << endl; } } newPlayersJoin(playerList(),&newPlayerList,inactivateIds); kdDebug(11001) << " Master calculates how many players to activate client has cnt=" << cnt << endl; kdDebug(11001) << " The game has " << playerCount() << " active players" << endl; kdDebug(11001) << " The user deactivated "<< inactivateIds.count() << " player already " << endl; kdDebug(11001) << " MaxPlayers for this game is " << maxPlayers() << endl; // Do we have too many players? (After the programmer disabled some?) // MH: We cannot use have player here as it CHANGES in the loop // int havePlayers = cnt+playerCount()-inactivateIds.count(); kdDebug(11001) << " havePlayers " << cnt+playerCount()-inactivateIds.count() << endl; while (maxPlayers() > 0 && maxPlayers() < (int)(cnt+playerCount() - inactivateIds.count())) { kdDebug(11001) << " Still to deacticvate " << (int)(cnt+playerCount()-inactivateIds.count())-(int)maxPlayers() << endl; KPlayer *currentPlayer=0; int currentPriority=0x7fff; // MAX_UINT (16bit?) to get the maximum of the list // find lowest network priority which is not yet in the newPlayerList // do this for the new players for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() ) { // Already in the list if (inactivateIds.find(player->id())!=inactivateIds.end()) { continue; } if (player->networkPriority()networkPriority(); currentPlayer=player; } } // find lowest network priority which is not yet in the newPlayerList // Do this for the network players for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) { // Already in the list if (inactivateIds.find(player->id())!=inactivateIds.end()) { continue; } if (player->networkPriority()networkPriority(); currentPlayer=player; } } // add it to inactivateIds if (currentPlayer) { kdDebug(11001) << "Marking player " << currentPlayer->id() << " for inactivation" << endl; inactivateIds.append(currentPlayer->id()); } else { kdError(11001) << "Couldn't find a player to dectivate..That is not so good..." << endl; break; } } kdDebug(11001) << "Alltogether deactivated " << inactivateIds.count() << " players" << endl; TQValueList::Iterator it; for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it ) { int pid=*it; kdDebug(11001) << " pid=" << pid << endl; } // Now deactivate the network players from the inactivateId list //TQValueList::Iterator it; for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it ) { int pid=*it; if (KGameMessage::rawGameId(pid) == sender) { continue; // client's player } kdDebug(11001) << " -> the network needs to deactivate " << pid <id(), KGameMessage::IdInactivatePlayer); } } else { kdError(11001) << " We should deactivate a player, but cannot find it...not good." << endl; } } // Now send out the player list which the client can activate for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() ) { kdDebug(11001) << " newplayerlist contains " << player->id() << endl; // Only activate what is not in the list if (inactivateIds.find(player->id())!=inactivateIds.end()) { continue; } kdDebug(11001) << " -> the client can ******** reactivate ******** " << player->id() << endl; sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer, sender); } // Save the game over the network TQByteArray bufferS; TQDataStream streamS(bufferS,IO_WriteOnly); // Save game over netowrk and save players savegame(streamS,true,true); sendSystemMessage(streamS,KGameMessage::IdGameLoad,sender); // Only to the client first , as the client will add players sendSystemMessage(sender, KGameMessage::IdGameSetupDone, sender); } // called by the IdSetupGame Message - CLIENT SIDE // Client needs to prepare for network transfer void KGame::setupGame(TQ_UINT32 sender) { TQByteArray bufferS; TQDataStream streamS(bufferS,IO_WriteOnly); // Deactivate all players KGamePlayerList mTmpList(d->mPlayerList); // we need copy otherwise the removal crashes TQ_INT32 cnt=mTmpList.count(); kdDebug(11001) << "Client: playerlistcount=" << d->mPlayerList.count() << " tmplistcout=" << cnt << endl; streamS << cnt; TQPtrListIterator it(mTmpList); KPlayer *player; while (it.current()) { player=it.current(); systemInactivatePlayer(player); // Give the new game id to all players (which are inactivated now) player->setId(KGameMessage::createPlayerId(player->id(),gameId())); // Save it for the master to decide what to do savePlayer(streamS,player); ++it; --cnt; } if (d->mPlayerList.count() > 0 || cnt!=0) { kdFatal(11001) << "KGame::setupGame(): Player list is not empty! or cnt!=0=" <mRandom->getLong(65535); sendSystemMessage(newseed,KGameMessage::IdSyncRandom); // Broadcast d->mRandom->setSeed(newseed); } void KGame::Debug() { KGameNetwork::Debug(); kdDebug(11001) << "------------------- KGAME -------------------------" << endl; kdDebug(11001) << "this: " << this << endl; kdDebug(11001) << "uniquePlayer " << d->mUniquePlayerNumber << endl; kdDebug(11001) << "gameStatus " << gameStatus() << endl; kdDebug(11001) << "MaxPlayers : " << maxPlayers() << endl; kdDebug(11001) << "NoOfPlayers : " << playerCount() << endl; kdDebug(11001) << "NoOfInactive: " << d->mInactivePlayerList.count() << endl; kdDebug(11001) << "---------------------------------------------------" << endl; } void KGame::slotClientConnected(TQ_UINT32 clientID) { if (isAdmin()) { negotiateNetworkGame(clientID); } } void KGame::slotServerDisconnected() // Client side { kdDebug(11001) << "======= SERVER DISCONNECT ======="<mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) { // TODO: CHECK: id=0, could not connect to server in the first place?? if (KGameMessage::rawGameId(player->id()) != gameId() && gameId()!=0) { kdDebug(11001) << "Player " << player->id() << " belongs to a removed game" << endl; removeList.append(player); } } for ( player=removeList.first(); player != 0; player=removeList.next() ) { bool remove = true; emit signalReplacePlayerIO(player, &remove); if (remove) { kdDebug(11001) << " ---> Removing player " << player->id() << endl; systemRemovePlayer(player,true); // no network necessary } } setMaster(); kdDebug(11001) << " our game id is after setMaster " << gameId() << endl; KGamePlayerList mReList(d->mInactivePlayerList); for ( player=mReList.first(); player != 0; player=mReList.next() ) { // TODO ?check for priority? Sequence should be ok if ((int)playerCount()id() << " as we are now local" << endl; } // TODO clear inactive lists ? Debug(); for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) { player->Debug(); } kdDebug(11001) << "+++++++++++" << k_funcinfo << " DONE=" << endl; emit signalClientLeftGame(0,oldgamestatus,this); } void KGame::slotClientDisconnected(TQ_UINT32 clientID,bool /*broken*/) // server side { kdDebug(11001) << "++++(SERVER)+++++++" << k_funcinfo << " clientId=" << clientID << endl; int oldgamestatus=gameStatus(); KPlayer *player; KGamePlayerList removeList; kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl; for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) { if (KGameMessage::rawGameId(player->id())==clientID) { kdDebug(11001) << "Player " << player->id() << " belongs to the removed game" << endl; removeList.append(player); } } for ( player=removeList.first(); player != 0; player=removeList.next() ) { // try to replace the KGameIO first bool remove = true; emit signalReplacePlayerIO(player, &remove); if (remove) { // otherwise (no new KGameIO) remove the player kdDebug(11001) << " ---> Removing player " << player->id() << endl; removePlayer(player,0); } } // Now add inactive players - sequence should be ok // TODO remove players from removed game for (unsigned int idx=0;idxmInactiveIdList.count();idx++) { TQValueList::Iterator it1 = d->mInactiveIdList.at(idx); player = findPlayer(*it1); if (((int)playerCount() < maxPlayers() || maxPlayers() < 0) && player && KGameMessage::rawGameId(*it1) != clientID) { activatePlayer(player); } } emit signalClientLeftGame(clientID,oldgamestatus,this); } // -------------------- Synchronisation ----------------------- // this initializes a newly connected client. // we send the number of players (including type) as well as game status and // properties to the client. After the initialization has been completed both // clients should have the same status (ie players, properties, etc) void KGame::negotiateNetworkGame(TQ_UINT32 clientID) { kdDebug(11001) << "===========================" << k_funcinfo << ": clientID=" << clientID << " =========================== "<< endl; if (!isAdmin()) { kdError(11001) << k_funcinfo << ": Serious WARNING..only gameAdmin should call this" << endl; return ; } TQByteArray buffer; TQDataStream streamGS(buffer,IO_WriteOnly); // write Game setup specific data //streamGS << (TQ_INT32)maxPlayers(); //streamGS << (TQ_INT32)minPlayers(); // send to the newly connected client *only* TQ_INT16 v=KGameMessage::version(); TQ_INT32 c=cookie(); streamGS << v << c; sendSystemMessage(streamGS, KGameMessage::IdSetupGame, clientID); } bool KGame::sendGroupMessage(const TQByteArray &msg, int msgid, TQ_UINT32 sender, const TQString& group) { // AB: group must not be i18n'ed!! we should better use an id for group and use // a groupName() for the name // FIXME KPlayer *player; for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) { if (player && player->group()==group) { sendMessage(msg,msgid,player->id(), sender); } } return true; } bool KGame::sendGroupMessage(const TQDataStream &msg, int msgid, TQ_UINT32 sender, const TQString& group) { return sendGroupMessage(((TQBuffer*)msg.device())->buffer(), msgid, sender, group); } bool KGame::sendGroupMessage(const TQString& msg, int msgid, TQ_UINT32 sender, const TQString& group) { TQByteArray buffer; TQDataStream stream(buffer, IO_WriteOnly); stream << msg; return sendGroupMessage(stream, msgid, sender, group); } bool KGame::addProperty(KGamePropertyBase* data) { return dataHandler()->addProperty(data); } bool KGame::sendPlayerProperty(int msgid, TQDataStream& s, TQ_UINT32 playerId) { return sendSystemMessage(s, msgid, playerId); } void KGame::sendProperty(int msgid, TQDataStream& stream, bool* sent) { bool s = sendSystemMessage(stream, msgid); if (s) { *sent = true; } } void KGame::emitSignal(KGamePropertyBase *me) { emit signalPropertyChanged(me,this); } KGamePropertyBase* KGame::findProperty(int id) const { return d->mProperties->find(id); } KGame::GamePolicy KGame::policy() const { return d->mPolicy; } void KGame::setPolicy(GamePolicy p,bool recursive) { // Set KGame policy d->mPolicy=p; if (recursive) { // Set all KGame property policy dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); // Set all KPLayer (active or inactive) property policy for (TQPtrListIterator it(d->mPlayerList); it.current(); ++it) { it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); } for (TQPtrListIterator it(d->mInactivePlayerList); it.current(); ++it) { it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); } } } /* * vim: et sw=2 */