You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegames/libtdegames/kgame/kgamenetwork.cpp

513 lines
16 KiB

/*
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 "kgamenetwork.h"
#include "kgamenetwork.moc"
#include "kgamemessage.h"
#include "kgameerror.h"
#include "kmessageserver.h"
#include "kmessageclient.h"
#include "kmessageio.h"
#include <dnssd/publicservice.h>
#include <kdebug.h>
#include <tqbuffer.h>
class KGameNetworkPrivate
{
public:
KGameNetworkPrivate()
{
mMessageClient = 0;
mMessageServer = 0;
mDisconnectId = 0;
mService = 0;
}
public:
KMessageClient* mMessageClient;
KMessageServer* mMessageServer;
TQ_UINT32 mDisconnectId; // Stores gameId() over a disconnect process
DNSSD::PublicService* mService;
TQString mType;
TQString mName;
int mCookie;
};
// ------------------- NETWORK GAME ------------------------
KGameNetwork::KGameNetwork(int c, TQObject* parent) : TQObject(parent, 0)
{
d = new KGameNetworkPrivate;
d->mCookie = (TQ_INT16)c;
// Init the game as a local game, i.e.
// create your own KMessageServer and a KMessageClient connected to it.
setMaster();
kdDebug(11001) << k_funcinfo << "this=" << this <<", cookie=" << cookie() << " sizeof(this)="<<sizeof(KGameNetwork) << endl;
}
KGameNetwork::~KGameNetwork()
{
kdDebug(11001) << k_funcinfo << "this=" << this << endl;
// Debug();
delete d->mService;
delete d;
}
// ----------------------------- status methods
bool KGameNetwork::isNetwork() const
{ return isOfferingConnections() || d->mMessageClient->isNetwork();}
TQ_UINT32 KGameNetwork::gameId() const
{
//return d->mMessageClient->id() ;
// Return stored id in the case of disconnect. In any other
// case the disconnect id is 0
if (d->mMessageClient->id()!=0 ) {
return d->mMessageClient->id() ;
} else {
return d->mDisconnectId;
}
}
int KGameNetwork::cookie() const
{ return d->mCookie; }
bool KGameNetwork::isMaster() const
{ return (d->mMessageServer != 0); }
bool KGameNetwork::isAdmin() const
{ return (d->mMessageClient->isAdmin()); }
KMessageClient* KGameNetwork::messageClient() const
{ return d->mMessageClient; }
KMessageServer* KGameNetwork::messageServer() const
{ return d->mMessageServer; }
// ----------------------- network init
void KGameNetwork::setMaster()
{
if (!d->mMessageServer) {
d->mMessageServer = new KMessageServer (cookie(), this);
} else {
kdWarning(11001) << k_funcinfo << "Server already running!!" << endl;
}
if (!d->mMessageClient) {
d->mMessageClient = new KMessageClient (this);
connect (d->mMessageClient, TQ_SIGNAL(broadcastReceived(const TQByteArray&, TQ_UINT32)),
this, TQ_SLOT(receiveNetworkTransmission(const TQByteArray&, TQ_UINT32)));
connect (d->mMessageClient, TQ_SIGNAL(connectionBroken()),
this, TQ_SIGNAL(signalConnectionBroken()));
connect (d->mMessageClient, TQ_SIGNAL(aboutToDisconnect(TQ_UINT32)),
this, TQ_SLOT(aboutToLoseConnection(TQ_UINT32)));
connect (d->mMessageClient, TQ_SIGNAL(connectionBroken()),
this, TQ_SLOT(slotResetConnection()));
connect (d->mMessageClient, TQ_SIGNAL(adminStatusChanged(bool)),
this, TQ_SLOT(slotAdminStatusChanged(bool)));
connect (d->mMessageClient, TQ_SIGNAL(eventClientConnected(TQ_UINT32)),
this, TQ_SIGNAL(signalClientConnected(TQ_UINT32)));
connect (d->mMessageClient, TQ_SIGNAL(eventClientDisconnected(TQ_UINT32, bool)),
this, TQ_SIGNAL(signalClientDisconnected(TQ_UINT32, bool)));
// broacast and direct messages are treated equally on receive.
connect (d->mMessageClient, TQ_SIGNAL(forwardReceived(const TQByteArray&, TQ_UINT32, const TQValueList<TQ_UINT32>&)),
d->mMessageClient, TQ_SIGNAL(broadcastReceived(const TQByteArray&, TQ_UINT32)));
} else {
// should be no problem but still has to be tested
kdDebug(11001) << k_funcinfo << "Client already exists!" << endl;
}
d->mMessageClient->setServer(d->mMessageServer);
}
void KGameNetwork::setDiscoveryInfo(const TQString& type, const TQString& name)
{
kdDebug() << k_funcinfo << type << ":" << name << endl;
d->mType = type;
d->mName = name;
tryPublish();
}
void KGameNetwork::tryPublish()
{
if (d->mType.isNull() || !isOfferingConnections()) return;
if (!d->mService) d->mService = new DNSSD::PublicService(d->mName,d->mType,port());
else {
if (d->mType!=d->mService->type()) d->mService->setType(d->mType);
if (d->mName!=d->mService->serviceName()) d->mService->setServiceName(d->mName);
}
if (!d->mService->isPublished()) d->mService->publishAsync();
}
void KGameNetwork::tryStopPublishing()
{
if (d->mService) d->mService->stop();
}
bool KGameNetwork::offerConnections(TQ_UINT16 port)
{
kdDebug (11001) << k_funcinfo << "on port " << port << endl;
if (!isMaster()) {
setMaster();
}
// Make sure this is 0
d->mDisconnectId = 0;
// FIXME: This debug message can be removed when the program is working correct.
if (d->mMessageServer && d->mMessageServer->isOfferingConnections()) {
kdDebug (11001) << k_funcinfo << "Already running as server! Changing the port now!" << endl;
}
tryStopPublishing();
kdDebug (11001) << k_funcinfo << "before Server->initNetwork" << endl;
if (!d->mMessageServer->initNetwork (port)) {
kdError (11001) << k_funcinfo << "Unable to bind to port " << port << "!" << endl;
// no need to delete - we just cannot listen to the port
// delete d->mMessageServer;
// d->mMessageServer = 0;
// d->mMessageClient->setServer((KMessageServer*)0);
return false;
}
kdDebug (11001) << k_funcinfo << "after Server->initNetwork" << endl;
tryPublish();
return true;
}
bool KGameNetwork::connectToServer (const TQString& host, TQ_UINT16 port)
{
if (host.isEmpty()) {
kdError(11001) << k_funcinfo << "No hostname given" << endl;
return false;
}
// Make sure this is 0
d->mDisconnectId = 0;
// if (!d->mMessageServer) {
// // FIXME: What shall we do here? Probably must stop a running game.
// kdWarning (11001) << k_funcinfo << "We are already connected to another server!" << endl;
/// }
if (d->mMessageServer) {
// FIXME: What shall we do here? Probably must stop a running game.
kdWarning(11001) << "we are server but we are trying to connect to another server! "
<< "make sure that all clients connect to that server! "
<< "quitting the local server now..." << endl;
stopServerConnection();
d->mMessageClient->setServer((KMessageIO*)0);
delete d->mMessageServer;
d->mMessageServer = 0;
}
kdDebug(11001) << " about to set server" << endl;
d->mMessageClient->setServer(host, port);
emit signalAdminStatusChanged(false); // as we delete the connection above isAdmin() is always false now!
// OK: We say that we already have connected, but this isn't so yet!
// If the connection cannot be established, it will look as being disconnected
// again ("slotConnectionLost" is called).
// Shall we differ between these?
kdDebug(11001) << "connected to " << host << ":" << port << endl;
return true;
}
TQ_UINT16 KGameNetwork::port() const
{
if (isNetwork()) {
if (isOfferingConnections()) {
return d->mMessageServer->serverPort();
} else {
return d->mMessageClient->peerPort();
}
}
return 0;
}
TQString KGameNetwork::hostName() const
{
return d->mMessageClient->peerName();
}
bool KGameNetwork::stopServerConnection()
{
// We still are the Master, we just don't accept further connections!
tryStopPublishing();
if (d->mMessageServer) {
d->mMessageServer->stopNetwork();
return true;
}
return false;
}
bool KGameNetwork::isOfferingConnections() const
{ return (d->mMessageServer && d->mMessageServer->isOfferingConnections()); }
void KGameNetwork::disconnect()
{
// TODO MH
kdDebug(11001) << k_funcinfo << endl;
stopServerConnection();
if (d->mMessageServer) {
TQValueList <TQ_UINT32> list=d->mMessageServer->clientIDs();
TQValueList<TQ_UINT32>::Iterator it;
for( it = list.begin(); it != list.end(); ++it )
{
kdDebug(11001) << "Client id=" << (*it) << endl;
KMessageIO *client=d->mMessageServer->findClient(*it);
if (!client)
{
continue;
}
kdDebug(11001) << " rtti=" << client->rtti() << endl;
if (client->rtti()==2)
{
kdDebug(11001) << "DIRECT IO " << endl;
}
else
{
d->mMessageServer->removeClient(client,false);
}
}
}
else
{
kdDebug(11001) << k_funcinfo << "before client->disconnect() id="<<gameId()<< endl;
//d->mMessageClient->setServer((KMessageIO*)0);
kdDebug(11001) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;
d->mMessageClient->disconnect();
kdDebug(11001) << "++++++--------------------------------------------+++++"<<endl;
}
//setMaster();
/*
if (d->mMessageServer) {
//delete d->mMessageServer;
//d->mMessageServer=0;
server=true;
kdDebug(11001) << " server true" << endl;
d->mMessageServer->deleteClients();
kdDebug(11001) << " server deleteClients" << endl;
}
*/
kdDebug(11001) << k_funcinfo << "DONE" << endl;
}
void KGameNetwork::aboutToLoseConnection(TQ_UINT32 clientID)
{
kdDebug(11001) << "Storing client id of connection "<<clientID<<endl;
d->mDisconnectId = clientID;
}
void KGameNetwork::slotResetConnection()
{
kdDebug(11001) << "Resseting client disconnect id"<<endl;
d->mDisconnectId = 0;
}
void KGameNetwork::electAdmin(TQ_UINT32 clientID)
{
if (!isAdmin()) {
kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl;
return;
}
TQByteArray buffer;
TQDataStream stream(buffer,IO_WriteOnly);
stream << static_cast<TQ_UINT32>( KMessageServer::REQ_ADMIN_CHANGE );
stream << clientID;
d->mMessageClient->sendServerMessage(buffer);
}
void KGameNetwork::setMaxClients(int max)
{
if (!isAdmin()) {
kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl;
return;
}
TQByteArray buffer;
TQDataStream stream(buffer,IO_WriteOnly);
stream << static_cast<TQ_UINT32>( KMessageServer::REQ_MAX_NUM_CLIENTS );
stream << (TQ_INT32)max;
d->mMessageClient->sendServerMessage(buffer);
}
void KGameNetwork::lock()
{
if (messageClient()) {
messageClient()->lock();
}
}
void KGameNetwork::unlock()
{
if (messageClient()) {
messageClient()->unlock();
}
}
// --------------------- send messages ---------------------------
bool KGameNetwork::sendSystemMessage(int data, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender)
{
TQByteArray buffer;
TQDataStream stream(buffer,IO_WriteOnly);
stream << data;
return sendSystemMessage(buffer,msgid,receiver,sender);
}
bool KGameNetwork::sendSystemMessage(const TQString &msg, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender)
{
TQByteArray buffer;
TQDataStream stream(buffer, IO_WriteOnly);
stream << msg;
return sendSystemMessage(buffer, msgid, receiver, sender);
}
bool KGameNetwork::sendSystemMessage(const TQDataStream &msg, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender)
{ return sendSystemMessage(((TQBuffer*)msg.device())->buffer(), msgid, receiver, sender); }
bool KGameNetwork::sendSystemMessage(const TQByteArray& data, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender)
{
TQByteArray buffer;
TQDataStream stream(buffer,IO_WriteOnly);
if (!sender) {
sender = gameId();
}
TQ_UINT32 receiverClient = KGameMessage::rawGameId(receiver); // KGame::gameId()
int receiverPlayer = KGameMessage::rawPlayerId(receiver); // KPlayer::id()
KGameMessage::createHeader(stream, sender, receiver, msgid);
stream.writeRawBytes(data.data(), data.size());
/*
kdDebug(11001) << "transmitGameClientMessage msgid=" << msgid << " recv="
<< receiver << " sender=" << sender << " Buffersize="
<< buffer.size() << endl;
*/
if (!d->mMessageClient) {
// No client created, this should never happen!
// Having a local game means we have our own
// KMessageServer and we are the only client.
kdWarning (11001) << k_funcinfo << "We don't have a client! Should never happen!" << endl;
return false;
}
if (receiverClient == 0 || receiverPlayer != 0)
{
// if receiverClient == 0 this is a broadcast message. if it is != 0 but
// receiverPlayer is also != 0 we have to send broadcast anyway, because the
// KPlayer object on all clients needs to receive the message.
d->mMessageClient->sendBroadcast(buffer);
}
else
{
d->mMessageClient->sendForward(buffer, receiverClient);
}
return true;
}
bool KGameNetwork::sendMessage(int data, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender)
{ return sendSystemMessage(data,msgid+KGameMessage::IdUser,receiver,sender); }
bool KGameNetwork::sendMessage(const TQString &msg, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender)
{ return sendSystemMessage(msg,msgid+KGameMessage::IdUser,receiver,sender); }
bool KGameNetwork::sendMessage(const TQDataStream &msg, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender)
{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
bool KGameNetwork::sendMessage(const TQByteArray &msg, int msgid, TQ_UINT32 receiver, TQ_UINT32 sender)
{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
void KGameNetwork::sendError(int error,const TQByteArray& message, TQ_UINT32 receiver, TQ_UINT32 sender)
{
TQByteArray buffer;
TQDataStream stream(buffer,IO_WriteOnly);
stream << (TQ_INT32) error;
stream.writeRawBytes(message.data(), message.size());
sendSystemMessage(stream,KGameMessage::IdError,receiver,sender);
}
// ----------------- receive messages from the network
void KGameNetwork::receiveNetworkTransmission(const TQByteArray& receiveBuffer, TQ_UINT32 clientID)
{
TQDataStream stream(receiveBuffer, IO_ReadOnly);
int msgid;
TQ_UINT32 sender; // the id of the KGame/KPlayer who sent the message
TQ_UINT32 receiver; // the id of the KGame/KPlayer the message is for
KGameMessage::extractHeader(stream, sender, receiver, msgid);
// kdDebug(11001) << k_funcinfo << "id=" << msgid << " sender=" << sender << " recv=" << receiver << endl;
// No broadcast : receiver==0
// No player isPlayer(receiver)
// Different game gameId()!=receiver
if (receiver && receiver!=gameId() && !KGameMessage::isPlayer(receiver) )
{
// receiver=0 is broadcast or player message
kdDebug(11001) << k_funcinfo << "Message not meant for us "
<< gameId() << "!=" << receiver << " rawid="
<< KGameMessage::rawGameId(receiver) << endl;
return;
}
else if (msgid==KGameMessage::IdError)
{
TQString text;
TQ_INT32 error;
stream >> error;
kdDebug(11001) << k_funcinfo << "Got IdError " << error << endl;
text = KGameError::errorText(error, stream);
kdDebug(11001) << "Error text: " << text.latin1() << endl;
emit signalNetworkErrorMessage((int)error,text);
}
else
{
networkTransmission(stream, msgid, receiver, sender, clientID);
}
}
// -------------- slots for the signals of the client
void KGameNetwork::slotAdminStatusChanged(bool isAdmin)
{
emit signalAdminStatusChanged(isAdmin);
// TODO: I'm pretty sure there are a lot of things that should be done here...
}
void KGameNetwork::Debug()
{
kdDebug(11001) << "------------------- KNETWORKGAME -------------------------" << endl;
kdDebug(11001) << "gameId " << gameId() << endl;
kdDebug(11001) << "gameMaster " << isMaster() << endl;
kdDebug(11001) << "gameAdmin " << isAdmin() << endl;
kdDebug(11001) << "---------------------------------------------------" << endl;
}