/* 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 "kgamenetwork.h" #include "kgamenetwork.moc" #include "kgamemessage.h" #include "kgameerror.h" #include "kmessageserver.h" #include "kmessageclient.h" #include "kmessageio.h" #include #include #include class KGameNetworkPrivate { public: KGameNetworkPrivate() { mMessageClient = 0; mMessageServer = 0; mDisconnectId = 0; mService = 0; } public: KMessageClient* mMessageClient; KMessageServer* mMessageServer; Q_UINT32 mDisconnectId; // Stores gameId() over a disconnect process DNSSD::PublicService* mService; QString mType; QString mName; int mCookie; }; // ------------------- NETWORK GAME ------------------------ KGameNetwork::KGameNetwork(int c, QObject* parent) : QObject(parent, 0) { d = new KGameNetworkPrivate; d->mCookie = (Q_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)="<mMessageClient) { d->mMessageClient = new KMessageClient (this); connect (d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)), this, SLOT(receiveNetworkTransmission(const QByteArray&, Q_UINT32))); connect (d->mMessageClient, SIGNAL(connectionBroken()), this, SIGNAL(signalConnectionBroken())); connect (d->mMessageClient, SIGNAL(aboutToDisconnect(Q_UINT32)), this, SLOT(aboutToLoseConnection(Q_UINT32))); connect (d->mMessageClient, SIGNAL(connectionBroken()), this, SLOT(slotResetConnection())); connect (d->mMessageClient, SIGNAL(adminStatusChanged(bool)), this, SLOT(slotAdminStatusChanged(bool))); connect (d->mMessageClient, SIGNAL(eventClientConnected(Q_UINT32)), this, SIGNAL(signalClientConnected(Q_UINT32))); connect (d->mMessageClient, SIGNAL(eventClientDisconnected(Q_UINT32, bool)), this, SIGNAL(signalClientDisconnected(Q_UINT32, bool))); // broacast and direct messages are treated equally on receive. connect (d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList&)), d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_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 QString& type, const QString& 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(Q_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 QString& host, Q_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; } Q_UINT16 KGameNetwork::port() const { if (isNetwork()) { if (isOfferingConnections()) { return d->mMessageServer->serverPort(); } else { return d->mMessageClient->peerPort(); } } return 0; } QString 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) { QValueList list=d->mMessageServer->clientIDs(); QValueList::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="<mMessageClient->setServer((KMessageIO*)0); kdDebug(11001) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++"<mMessageClient->disconnect(); kdDebug(11001) << "++++++--------------------------------------------+++++"<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(Q_UINT32 clientID) { kdDebug(11001) << "Storing client id of connection "<mDisconnectId = clientID; } void KGameNetwork::slotResetConnection() { kdDebug(11001) << "Resseting client disconnect id"<mDisconnectId = 0; } void KGameNetwork::electAdmin(Q_UINT32 clientID) { if (!isAdmin()) { kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl; return; } QByteArray buffer; QDataStream stream(buffer,IO_WriteOnly); stream << static_cast( 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; } QByteArray buffer; QDataStream stream(buffer,IO_WriteOnly); stream << static_cast( KMessageServer::REQ_MAX_NUM_CLIENTS ); stream << (Q_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, Q_UINT32 receiver, Q_UINT32 sender) { QByteArray buffer; QDataStream stream(buffer,IO_WriteOnly); stream << data; return sendSystemMessage(buffer,msgid,receiver,sender); } bool KGameNetwork::sendSystemMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) { QByteArray buffer; QDataStream stream(buffer, IO_WriteOnly); stream << msg; return sendSystemMessage(buffer, msgid, receiver, sender); } bool KGameNetwork::sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) { return sendSystemMessage(((QBuffer*)msg.device())->buffer(), msgid, receiver, sender); } bool KGameNetwork::sendSystemMessage(const QByteArray& data, int msgid, Q_UINT32 receiver, Q_UINT32 sender) { QByteArray buffer; QDataStream stream(buffer,IO_WriteOnly); if (!sender) { sender = gameId(); } Q_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, Q_UINT32 receiver, Q_UINT32 sender) { return sendSystemMessage(data,msgid+KGameMessage::IdUser,receiver,sender); } bool KGameNetwork::sendMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) { return sendSystemMessage(msg,msgid+KGameMessage::IdUser,receiver,sender); } bool KGameNetwork::sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) { return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); } bool KGameNetwork::sendMessage(const QByteArray &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) { return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); } void KGameNetwork::sendError(int error,const QByteArray& message, Q_UINT32 receiver, Q_UINT32 sender) { QByteArray buffer; QDataStream stream(buffer,IO_WriteOnly); stream << (Q_INT32) error; stream.writeRawBytes(message.data(), message.size()); sendSystemMessage(stream,KGameMessage::IdError,receiver,sender); } // ----------------- receive messages from the network void KGameNetwork::receiveNetworkTransmission(const QByteArray& receiveBuffer, Q_UINT32 clientID) { QDataStream stream(receiveBuffer, IO_ReadOnly); int msgid; Q_UINT32 sender; // the id of the KGame/KPlayer who sent the message Q_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) { QString text; Q_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; } /* * vim: et sw=2 */