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.
513 lines
16 KiB
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, TQT_SIGNAL(broadcastReceived(const TQByteArray&, TQ_UINT32)),
|
|
this, TQT_SLOT(receiveNetworkTransmission(const TQByteArray&, TQ_UINT32)));
|
|
connect (d->mMessageClient, TQT_SIGNAL(connectionBroken()),
|
|
this, TQT_SIGNAL(signalConnectionBroken()));
|
|
connect (d->mMessageClient, TQT_SIGNAL(aboutToDisconnect(TQ_UINT32)),
|
|
this, TQT_SLOT(aboutToLoseConnection(TQ_UINT32)));
|
|
connect (d->mMessageClient, TQT_SIGNAL(connectionBroken()),
|
|
this, TQT_SLOT(slotResetConnection()));
|
|
|
|
connect (d->mMessageClient, TQT_SIGNAL(adminStatusChanged(bool)),
|
|
this, TQT_SLOT(slotAdminStatusChanged(bool)));
|
|
connect (d->mMessageClient, TQT_SIGNAL(eventClientConnected(TQ_UINT32)),
|
|
this, TQT_SIGNAL(signalClientConnected(TQ_UINT32)));
|
|
connect (d->mMessageClient, TQT_SIGNAL(eventClientDisconnected(TQ_UINT32, bool)),
|
|
this, TQT_SIGNAL(signalClientDisconnected(TQ_UINT32, bool)));
|
|
|
|
// broacast and direct messages are treated equally on receive.
|
|
connect (d->mMessageClient, TQT_SIGNAL(forwardReceived(const TQByteArray&, TQ_UINT32, const TQValueList<TQ_UINT32>&)),
|
|
d->mMessageClient, TQT_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;
|
|
}
|