|
|
|
/*
|
|
|
|
This file is part of the KDE 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 <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <tqbuffer.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
#include <tqptrqueue.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <krandomsequence.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#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<KPlayer> 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<int> 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(Q_UINT32)),
|
|
|
|
this, TQT_SLOT(slotClientConnected(Q_UINT32)));
|
|
|
|
connect(this, TQT_SIGNAL(signalClientDisconnected(Q_UINT32,bool)),
|
|
|
|
this, TQT_SLOT(slotClientDisconnected(Q_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
|
|
|
|
Q_INT32 c;
|
|
|
|
stream >> c; // cookie
|
|
|
|
|
|
|
|
if (c!=cookie())
|
|
|
|
{
|
|
|
|
kdWarning(11001) << "Trying to load different game version we="<<cookie() << " saved=" << c << endl;
|
|
|
|
bool result=false;
|
|
|
|
emit signalLoadError(stream,network,(int)c,result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (resetgame) reset();
|
|
|
|
|
|
|
|
uint i;
|
|
|
|
stream >> i;
|
|
|
|
// setPolicy((GamePolicy)i);
|
|
|
|
|
|
|
|
stream >> d->mUniquePlayerNumber;
|
|
|
|
|
|
|
|
if (gameSequence())
|
|
|
|
{
|
|
|
|
gameSequence()->setCurrentPlayer(0); // TODO !!!
|
|
|
|
}
|
|
|
|
int newseed;
|
|
|
|
stream >> newseed;
|
|
|
|
d->mRandom->setSeed(newseed);
|
|
|
|
|
|
|
|
// Switch off the direct emitting of signals while
|
|
|
|
// loading properties. This can cause inconsistencies
|
|
|
|
// otherwise if a property emits and this emit accesses
|
|
|
|
// a property not yet loaded
|
|
|
|
// Note we habe to have this external locking to prevent the games unlocking
|
|
|
|
// to access the players
|
|
|
|
dataHandler()->lockDirectEmit();
|
|
|
|
KPlayer *player;
|
|
|
|
for ( player=playerList()->first(); player != 0; player=playerList()->next() )
|
|
|
|
{
|
|
|
|
player->dataHandler()->lockDirectEmit();
|
|
|
|
// kdDebug(11001) << "Player "<<player->id() << " to indirect emit" <<endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Properties
|
|
|
|
dataHandler()->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<playercount;i++)
|
|
|
|
{
|
|
|
|
KPlayer *newplayer=loadPlayer(stream,network);
|
|
|
|
systemAddPlayer(newplayer);
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_INT16 cookie;
|
|
|
|
stream >> cookie;
|
|
|
|
if (cookie==KGAME_LOAD_COOKIE) {
|
|
|
|
kdDebug(11001) << " Game loaded propertly"<<endl;
|
|
|
|
} else {
|
|
|
|
kdError(11001) << " Game loading error. probably format error"<<endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch back on the direct emitting of signals and emit the
|
|
|
|
// queued signals.
|
|
|
|
// Note we habe to have this external locking to prevent the games unlocking
|
|
|
|
// to access the players
|
|
|
|
dataHandler()->unlockDirectEmit();
|
|
|
|
for ( player=playerList()->first(); player != 0; player=playerList()->next() )
|
|
|
|
{
|
|
|
|
player->dataHandler()->unlockDirectEmit();
|
|
|
|
// kdDebug(11001) << "Player "<<player->id() << " to direct emit" <<endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit signalLoad(stream);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KGame::save(TQString filename,bool saveplayers)
|
|
|
|
{
|
|
|
|
if (filename.isNull())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
TQFile f(filename);
|
|
|
|
if (!f.open(IO_WriteOnly))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
TQDataStream s( &f );
|
|
|
|
save(s,saveplayers);
|
|
|
|
f.close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KGame::save(TQDataStream &stream,bool saveplayers)
|
|
|
|
{ return savegame(stream, false,saveplayers); }
|
|
|
|
|
|
|
|
bool KGame::savegame(TQDataStream &stream,bool /*network*/,bool saveplayers)
|
|
|
|
{
|
|
|
|
// Save Game Data
|
|
|
|
|
|
|
|
// internal variables
|
|
|
|
Q_INT32 c=cookie();
|
|
|
|
stream << c;
|
|
|
|
|
|
|
|
uint p=(uint)policy();
|
|
|
|
stream << p;
|
|
|
|
stream << d->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 << (Q_INT16)KGAME_LOAD_COOKIE;
|
|
|
|
|
|
|
|
emit signalSave(stream);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KGame::savePlayer(TQDataStream &stream,KPlayer* p)
|
|
|
|
{
|
|
|
|
// this could be in KGameMessage as well
|
|
|
|
stream << (Q_INT32)p->rtti();
|
|
|
|
stream << (Q_INT32)p->id();
|
|
|
|
stream << (Q_INT32)p->calcIOValue();
|
|
|
|
p->save(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KGame::savePlayers(TQDataStream &stream, KGamePlayerList *list)
|
|
|
|
{
|
|
|
|
if (!list)
|
|
|
|
{
|
|
|
|
list=playerList();
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_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)
|
|
|
|
{
|
|
|
|
Q_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(Q_UINT32 id) const
|
|
|
|
{
|
|
|
|
for (TQPtrListIterator<KPlayer> it(d->mPlayerList); it.current(); ++it)
|
|
|
|
{
|
|
|
|
if (it.current()->id() == id)
|
|
|
|
{
|
|
|
|
return it.current();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (TQPtrListIterator<KPlayer> 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="<<maxPlayers()<<" playerCount="<<playerCount() << endl;
|
|
|
|
// Add the player to the game
|
|
|
|
d->mPlayerList.append(newplayer);
|
|
|
|
newplayer->setGame(this);
|
|
|
|
kdDebug(11001) << "Player: isVirtual=" << newplayer->isVirtual() << endl;
|
|
|
|
kdDebug(11001) << " id=" << newplayer->id() << " #Players="
|
|
|
|
<< d->mPlayerList.count() << " added " << newplayer
|
|
|
|
<< " (virtual=" << newplayer->isVirtual() << ")" << endl;
|
|
|
|
emit signalPlayerJoinedGame(newplayer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called by the KPlayer destructor
|
|
|
|
void KGame::playerDeleted(KPlayer *player)
|
|
|
|
{
|
|
|
|
kdDebug(11001) << k_funcinfo << ": id (" << player->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 "<<player->id() << endl;
|
|
|
|
sendSystemMessage(player->id(), KGameMessage::IdRemovePlayer, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KGame::removePlayer(KPlayer * player, Q_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 "<<player->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()<minPlayers())
|
|
|
|
{
|
|
|
|
kdWarning(11001) << k_funcinfo ": not enough players, PAUSING game\n" << endl;
|
|
|
|
setGameStatus(Pause);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KGame::systemRemove(KPlayer* p,bool deleteit)
|
|
|
|
{
|
|
|
|
if (!p)
|
|
|
|
{
|
|
|
|
kdWarning(11001) << "cannot remove NULL player" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool result;
|
|
|
|
kdDebug(11001) << k_funcinfo << ": Player (" << p->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, Q_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, Q_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"<<endl;
|
|
|
|
// TODO: (MH 03-2003): We need an return option from playerInput so that
|
|
|
|
// the player's is not automatically disabled here
|
|
|
|
if (!player->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 "<<player->id()<<endl;
|
|
|
|
// Check for game over and if not allow the next player to move
|
|
|
|
int gameOver = 0;
|
|
|
|
if (gameSequence())
|
|
|
|
{
|
|
|
|
gameSequence()->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()<minPlayers())
|
|
|
|
{
|
|
|
|
kdDebug(11001) << k_funcinfo << ": not enough players, pausing game\n" << endl;
|
|
|
|
status=Pause;
|
|
|
|
}
|
|
|
|
d->mGameStatus = status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KGame::networkTransmission(TQDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_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="<<msgid<<" recv=" << receiver << " sender=" << sender << endl;
|
|
|
|
|
|
|
|
|
|
|
|
// *first* notice the game that something has changed - so no return prevents
|
|
|
|
// this
|
|
|
|
emit signalMessageUpdate(msgid, receiver, sender);
|
|
|
|
if (KGameMessage::isPlayer(receiver))
|
|
|
|
{
|
|
|
|
//kdDebug(11001) << "message id " << msgid << " seems to be for a player ("<<active=p->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
|
|
|
|
{
|
|
|
|
Q_INT16 v;
|
|
|
|
Q_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="<<cookie()<<" master="<<c<<endl;
|
|
|
|
sendError(KGameError::Cookie, KGameError::errCookie(cookie(), c));
|
|
|
|
disconnect(); // disconnect from master
|
|
|
|
}
|
|
|
|
else if (v!=KGameMessage::version())
|
|
|
|
{
|
|
|
|
sendError(KGameError::Version, KGameError::errVersion(v));
|
|
|
|
disconnect(); // disconnect from master
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setupGame(sender);
|
|
|
|
}
|
|
|
|
kdDebug(11001) << "========== (Client) Setup game done\n";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case KGameMessage::IdSetupGameContinue: // Master: second step in game setup
|
|
|
|
{
|
|
|
|
kdDebug(11001) << "=====>(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, Q_UINT32 sender)
|
|
|
|
{
|
|
|
|
KPlayer *player;
|
|
|
|
Q_INT32 cnt;
|
|
|
|
int i;
|
|
|
|
stream >> cnt;
|
|
|
|
|
|
|
|
TQValueList<int> inactivateIds;
|
|
|
|
|
|
|
|
KGamePlayerList newPlayerList;
|
|
|
|
newPlayerList.setAutoDelete(true);
|
|
|
|
for (i=0;i<cnt;i++)
|
|
|
|
{
|
|
|
|
player=loadPlayer(stream,true);
|
|
|
|
kdDebug(11001) << " Master got player " << player->id() <<" 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()<currentPriority)
|
|
|
|
{
|
|
|
|
currentPriority=player->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()<currentPriority)
|
|
|
|
{
|
|
|
|
currentPriority=player->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<int>::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<int>::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 <<endl;
|
|
|
|
player=findPlayer(pid);
|
|
|
|
if (player)
|
|
|
|
{
|
|
|
|
// We have to make REALLY sure that the player is gone. With any policy
|
|
|
|
systemInactivatePlayer(player);
|
|
|
|
if (policy()!=PolicyLocal)
|
|
|
|
{
|
|
|
|
sendSystemMessage(player->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(Q_UINT32 sender)
|
|
|
|
{
|
|
|
|
TQByteArray bufferS;
|
|
|
|
TQDataStream streamS(bufferS,IO_WriteOnly);
|
|
|
|
|
|
|
|
// Deactivate all players
|
|
|
|
KGamePlayerList mTmpList(d->mPlayerList); // we need copy otherwise the removal crashes
|
|
|
|
Q_INT32 cnt=mTmpList.count();
|
|
|
|
kdDebug(11001) << "Client: playerlistcount=" << d->mPlayerList.count() << " tmplistcout=" << cnt << endl;
|
|
|
|
|
|
|
|
streamS << cnt;
|
|
|
|
|
|
|
|
TQPtrListIterator<KPlayer> 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=" <<cnt << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendSystemMessage(streamS,KGameMessage::IdSetupGameContinue,sender);
|
|
|
|
}
|
|
|
|
|
|
|
|
// unused by KGame
|
|
|
|
void KGame::syncRandom()
|
|
|
|
{
|
|
|
|
int newseed=(int)d->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(Q_UINT32 clientID)
|
|
|
|
{
|
|
|
|
if (isAdmin())
|
|
|
|
{
|
|
|
|
negotiateNetworkGame(clientID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KGame::slotServerDisconnected() // Client side
|
|
|
|
{
|
|
|
|
kdDebug(11001) << "======= SERVER DISCONNECT ======="<<endl;
|
|
|
|
kdDebug(11001) << "+++ (CLIENT)++++++++" << k_funcinfo << ": our GameID="<<gameId() << endl;
|
|
|
|
|
|
|
|
int oldgamestatus=gameStatus();
|
|
|
|
|
|
|
|
KPlayer *player;
|
|
|
|
KGamePlayerList removeList;
|
|
|
|
kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl;
|
|
|
|
kdDebug(11001) << "Inactive Playerlist of client=" << d->mInactivePlayerList.count() << " count" << endl;
|
|
|
|
for ( player=d->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()<maxPlayers() || maxPlayers()<0)
|
|
|
|
{
|
|
|
|
systemActivatePlayer(player);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kdDebug(11001) << " Players activated player-cnt=" << playerCount() << endl;
|
|
|
|
|
|
|
|
for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
|
|
|
|
{
|
|
|
|
int oldid=player->id();
|
|
|
|
d->mUniquePlayerNumber++;
|
|
|
|
player->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber,gameId()));
|
|
|
|
kdDebug(11001) << "Player id " << oldid <<" changed to " << player->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(Q_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;idx<d->mInactiveIdList.count();idx++)
|
|
|
|
{
|
|
|
|
TQValueList<int>::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(Q_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 << (Q_INT32)maxPlayers();
|
|
|
|
//streamGS << (Q_INT32)minPlayers();
|
|
|
|
|
|
|
|
// send to the newly connected client *only*
|
|
|
|
Q_INT16 v=KGameMessage::version();
|
|
|
|
Q_INT32 c=cookie();
|
|
|
|
streamGS << v << c;
|
|
|
|
sendSystemMessage(streamGS, KGameMessage::IdSetupGame, clientID);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KGame::sendGroupMessage(const TQByteArray &msg, int msgid, Q_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, Q_UINT32 sender, const TQString& group)
|
|
|
|
{ return sendGroupMessage(((TQBuffer*)msg.device())->buffer(), msgid, sender, group); }
|
|
|
|
|
|
|
|
bool KGame::sendGroupMessage(const TQString& msg, int msgid, Q_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, Q_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<KPlayer> it(d->mPlayerList); it.current(); ++it)
|
|
|
|
{
|
|
|
|
it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
|
|
|
|
}
|
|
|
|
for (TQPtrListIterator<KPlayer> it(d->mInactivePlayerList); it.current(); ++it)
|
|
|
|
{
|
|
|
|
it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vim: et sw=2
|
|
|
|
*/
|