KDEGames

kgamenetwork.cpp
1 /*
2  This file is part of the KDE games library
3  Copyright (C) 2001 Martin Heni (kde at heni-online.de)
4  Copyright (C) 2001 Andreas Beckermann ([email protected])
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License version 2 as published by the Free Software Foundation.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "kgamenetwork.h"
22 
23 #include "kgamemessage.h"
24 #include "kgameerror.h"
25 
26 #include "kmessageserver.h"
27 #include "kmessageclient.h"
28 #include "kmessageio.h"
29 #include <dnssd/publicservice.h>
30 
31 #include <QBuffer>
32 //Added by qt3to4:
33 #include <QList>
34 
35 class KGameNetworkPrivate
36 {
37 public:
38  KGameNetworkPrivate()
39  {
40  mMessageClient = nullptr;
41  mMessageServer = nullptr;
42  mDisconnectId = 0;
43  mService = nullptr;
44  }
45 
46 public:
47  KMessageClient* mMessageClient;
48  KMessageServer* mMessageServer;
49  quint32 mDisconnectId; // Stores gameId() over a disconnect process
50  KDNSSD::PublicService* mService;
51  QString mType;
52  QString mName;
53 
54  int mCookie;
55 };
56 
57 // ------------------- NETWORK GAME ------------------------
59  : QObject(parent),
60  d( new KGameNetworkPrivate )
61 {
62  d->mCookie = (qint16)c;
63 
64  // Init the game as a local game, i.e.
65  // create your own KMessageServer and a KMessageClient connected to it.
66  setMaster();
67 
68  qCDebug(GAMES_PRIVATE_KGAME) << "this=" << this <<", cookie=" << cookie() << "sizeof(this)="<<sizeof(KGameNetwork);
69 }
70 
71 KGameNetwork::~KGameNetwork()
72 {
73  qCDebug(GAMES_PRIVATE_KGAME) << "this=" << this;
74 // Debug();
75  delete d->mService;
76  delete d;
77 }
78 
79 // ----------------------------- status methods
81 { return isOfferingConnections() || d->mMessageClient->isNetwork();}
82 
83 quint32 KGameNetwork::gameId() const
84 {
85  //return d->mMessageClient->id() ;
86  // Return stored id in the case of disconnect. In any other
87  // case the disconnect id is 0
88  if (d->mMessageClient->id()!=0 ) {
89  return d->mMessageClient->id() ;
90  } else {
91  return d->mDisconnectId;
92  }
93 }
94 
96 { return d->mCookie; }
97 
99 { return (d->mMessageServer != nullptr); }
100 
102 { return (d->mMessageClient->isAdmin()); }
103 
105 { return d->mMessageClient; }
106 
108 { return d->mMessageServer; }
109 
110 // ----------------------- network init
112 {
113  if (!d->mMessageServer) {
114  d->mMessageServer = new KMessageServer (cookie(), this);
115  } else {
116  qCWarning(GAMES_PRIVATE_KGAME) << "Server already running!!";
117  }
118  if (!d->mMessageClient) {
119  d->mMessageClient = new KMessageClient (this);
124 
128 
129  // broacast and direct messages are treated equally on receive.
130  connect (d->mMessageClient, &KMessageClient::forwardReceived,
131  d->mMessageClient, &KMessageClient::broadcastReceived);
132 
133  } else {
134  // should be no problem but still has to be tested
135  qCDebug(GAMES_PRIVATE_KGAME) << "Client already exists!";
136  }
137  d->mMessageClient->setServer(d->mMessageServer);
138 }
139 
140 void KGameNetwork::setDiscoveryInfo(const QString& type, const QString& name)
141 {
142  qCDebug(GAMES_PRIVATE_KGAME) << type << ":" << name;
143  d->mType = type;
144  d->mName = name;
145  tryPublish();
146 }
147 
148 void KGameNetwork::tryPublish()
149 {
150  if (d->mType.isNull() || !isOfferingConnections()) return;
151  if (!d->mService) d->mService = new KDNSSD::PublicService(d->mName,d->mType,port());
152  else {
153  if (d->mType!=d->mService->type()) d->mService->setType(d->mType);
154  if (d->mName!=d->mService->serviceName()) d->mService->setServiceName(d->mName);
155  }
156  if (!d->mService->isPublished()) d->mService->publishAsync();
157 }
158 
159 void KGameNetwork::tryStopPublishing()
160 {
161  if (d->mService) d->mService->stop();
162 }
163 
165 {
166  qCDebug(GAMES_PRIVATE_KGAME) << "on port" << port;
167  if (!isMaster()) {
168  setMaster();
169  }
170 
171  // Make sure this is 0
172  d->mDisconnectId = 0;
173 
174  // FIXME: This debug message can be removed when the program is working correct.
175  if (d->mMessageServer && d->mMessageServer->isOfferingConnections()) {
176  qCDebug(GAMES_PRIVATE_KGAME) << "Already running as server! Changing the port now!";
177  }
178 
179  tryStopPublishing();
180  qCDebug(GAMES_PRIVATE_KGAME) << "before Server->initNetwork";
181  if (!d->mMessageServer->initNetwork (port)) {
182  qCCritical(GAMES_PRIVATE_KGAME) << "Unable to bind to port" << port << "!";
183  // no need to delete - we just cannot listen to the port
184 // delete d->mMessageServer;
185 // d->mMessageServer = 0;
186 // d->mMessageClient->setServer((KMessageServer*)0);
187  return false;
188  }
189  qCDebug(GAMES_PRIVATE_KGAME) << "after Server->initNetwork";
190  tryPublish();
191  return true;
192 }
193 
194 bool KGameNetwork::connectToServer (const QString& host, quint16 port)
195 {
196  if (host.isEmpty()) {
197  qCCritical(GAMES_PRIVATE_KGAME) << "No hostname given";
198  return false;
199  }
200  if (connectToServer(new KMessageSocket (host, port)))
201  {
202  qCDebug(GAMES_PRIVATE_KGAME) << "connected to" << host << ":" << port;
203  return true;
204  }
205  else
206  {
207  return false;
208  }
209 }
210 
212 {
213  // Make sure this is 0
214  d->mDisconnectId = 0;
215 
216  // if (!d->mMessageServer) {
217  // // FIXME: What shall we do here? Probably must stop a running game.
218  // qCWarning(GAMES_PRIVATE_KGAME) << "We are already connected to another server!";
220 
221  if (d->mMessageServer) {
222  // FIXME: What shall we do here? Probably must stop a running game.
223  qCWarning(GAMES_PRIVATE_KGAME) << "we are server but we are trying to connect to another server! "
224  << "make sure that all clients connect to that server! "
225  << "quitting the local server now...";
227  d->mMessageClient->setServer((KMessageIO*)nullptr);
228  delete d->mMessageServer;
229  d->mMessageServer = nullptr;
230  }
231 
232  qCDebug(GAMES_PRIVATE_KGAME) << " about to set server";
233  d->mMessageClient->setServer(connection);
234  Q_EMIT signalAdminStatusChanged(false); // as we delete the connection above isAdmin() is always false now!
235 
236  // OK: We say that we already have connected, but this isn't so yet!
237  // If the connection cannot be established, it will look as being disconnected
238  // again ("slotConnectionLost" is called).
239  // Shall we differ between these?
240  qCDebug(GAMES_PRIVATE_KGAME) << "connected";
241  return true;
242 }
243 
244 quint16 KGameNetwork::port() const
245 {
246  if (isNetwork()) {
247  if (isOfferingConnections()) {
248  return d->mMessageServer->serverPort();
249  } else {
250  return d->mMessageClient->peerPort();
251  }
252  }
253  return 0;
254 }
255 
257 {
258  return d->mMessageClient->peerName();
259 }
260 
262 {
263  // We still are the Master, we just don't accept further connections!
264  tryStopPublishing();
265  if (d->mMessageServer) {
266  d->mMessageServer->stopNetwork();
267  return true;
268  }
269  return false;
270 }
271 
273 { return (d->mMessageServer && d->mMessageServer->isOfferingConnections()); }
274 
276 {
277  // TODO MH
278  qCDebug(GAMES_PRIVATE_KGAME) ;
280  if (d->mMessageServer) {
281  QList <quint32> list=d->mMessageServer->clientIDs();
283  for( it = list.begin(); it != list.end(); ++it )
284  {
285  qCDebug(GAMES_PRIVATE_KGAME) << "Client id=" << (*it);
286  KMessageIO *client=d->mMessageServer->findClient(*it);
287  if (!client)
288  {
289  continue;
290  }
291  qCDebug(GAMES_PRIVATE_KGAME) << " rtti=" << client->rtti();
292  if (client->rtti()==2)
293  {
294  qCDebug(GAMES_PRIVATE_KGAME) << "DIRECT IO";
295  }
296  else
297  {
298  d->mMessageServer->removeClient(client,false);
299  }
300  }
301  }
302  else
303  {
304  qCDebug(GAMES_PRIVATE_KGAME) << "before client->disconnect() id="<<gameId();
305  //d->mMessageClient->setServer((KMessageIO*)0);
306  qCDebug(GAMES_PRIVATE_KGAME) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++";
307  d->mMessageClient->disconnect();
308 
309  qCDebug(GAMES_PRIVATE_KGAME) << "++++++--------------------------------------------+++++";
310  }
311  //setMaster();
312  /*
313  if (d->mMessageServer) {
314  //delete d->mMessageServer;
315  //d->mMessageServer=0;
316  server=true;
317  qCDebug(GAMES_PRIVATE_KGAME) << " server true";
318  d->mMessageServer->deleteClients();
319  qCDebug(GAMES_PRIVATE_KGAME) << " server deleteClients";
320  }
321  */
322  qCDebug(GAMES_PRIVATE_KGAME) << "DONE";
323 }
324 
326 {
327  qCDebug(GAMES_PRIVATE_KGAME) << "Storing client id of connection "<<clientID;
328  d->mDisconnectId = clientID;
329 }
330 
332 {
333  qCDebug(GAMES_PRIVATE_KGAME) << "Resseting client disconnect id";
334  d->mDisconnectId = 0;
335 }
336 
337 void KGameNetwork::electAdmin(quint32 clientID)
338 {
339  if (!isAdmin()) {
340  qCWarning(GAMES_PRIVATE_KGAME) << "only ADMIN is allowed to call this!";
341  return;
342  }
343  QByteArray buffer;
344  QDataStream stream(&buffer,QIODevice::WriteOnly);
345  stream << static_cast<quint32>( KMessageServer::REQ_ADMIN_CHANGE );
346  stream << clientID;
347  d->mMessageClient->sendServerMessage(buffer);
348 }
349 
351 {
352  if (!isAdmin()) {
353  qCWarning(GAMES_PRIVATE_KGAME) << "only ADMIN is allowed to call this!";
354  return;
355  }
356  QByteArray buffer;
357  QDataStream stream(&buffer,QIODevice::WriteOnly);
358  stream << static_cast<quint32>( KMessageServer::REQ_MAX_NUM_CLIENTS );
359  stream << (qint32)max;
360  d->mMessageClient->sendServerMessage(buffer);
361 }
362 
364 {
365  if (messageClient()) {
366  messageClient()->lock();
367  }
368 }
369 
371 {
372  if (messageClient()) {
373  messageClient()->unlock();
374  }
375 }
376 
377 // --------------------- send messages ---------------------------
378 
379 bool KGameNetwork::sendSystemMessage(int data, int msgid, quint32 receiver, quint32 sender)
380 {
381  QByteArray buffer;
382  QDataStream stream(&buffer,QIODevice::WriteOnly);
383  stream << data;
384  return sendSystemMessage(buffer,msgid,receiver,sender);
385 }
386 
387 bool KGameNetwork::sendSystemMessage(const QString &msg, int msgid, quint32 receiver, quint32 sender)
388 {
389  QByteArray buffer;
390  QDataStream stream(&buffer, QIODevice::WriteOnly);
391  stream << msg;
392  return sendSystemMessage(buffer, msgid, receiver, sender);
393 }
394 
395 bool KGameNetwork::sendSystemMessage(const QDataStream &msg, int msgid, quint32 receiver, quint32 sender)
396 { return sendSystemMessage(((QBuffer*)msg.device())->buffer(), msgid, receiver, sender); }
397 
398 bool KGameNetwork::sendSystemMessage(const QByteArray& data, int msgid, quint32 receiver, quint32 sender)
399 {
400  QByteArray buffer;
401  QDataStream stream(&buffer,QIODevice::WriteOnly);
402  if (!sender) {
403  sender = gameId();
404  }
405 
406  quint32 receiverClient = KGameMessage::rawGameId(receiver); // KGame::gameId()
407  int receiverPlayer = KGameMessage::rawPlayerId(receiver); // KPlayer::id()
408 
409  KGameMessage::createHeader(stream, sender, receiver, msgid);
410  stream.writeRawData(data.data(), data.size());
411 
412  /*
413  qCDebug(GAMES_PRIVATE_KGAME) << "transmitGameClientMessage msgid=" << msgid << "recv="
414  << receiver << "sender=" << sender << "Buffersize="
415  << buffer.size();
416  */
417 
418  if (!d->mMessageClient) {
419  // No client created, this should never happen!
420  // Having a local game means we have our own
421  // KMessageServer and we are the only client.
422  qCWarning(GAMES_PRIVATE_KGAME) << "We don't have a client! Should never happen!";
423  return false;
424  }
425 
426  if (receiverClient == 0 || receiverPlayer != 0)
427  {
428  // if receiverClient == 0 this is a broadcast message. if it is != 0 but
429  // receiverPlayer is also != 0 we have to send broadcast anyway, because the
430  // KPlayer object on all clients needs to receive the message.
431  d->mMessageClient->sendBroadcast(buffer);
432  }
433  else
434  {
435  d->mMessageClient->sendForward(buffer, receiverClient);
436  }
437  return true;
438 }
439 
440 bool KGameNetwork::sendMessage(int data, int msgid, quint32 receiver, quint32 sender)
441 { return sendSystemMessage(data,msgid+KGameMessage::IdUser,receiver,sender); }
442 
443 bool KGameNetwork::sendMessage(const QString &msg, int msgid, quint32 receiver, quint32 sender)
444 { return sendSystemMessage(msg,msgid+KGameMessage::IdUser,receiver,sender); }
445 
446 bool KGameNetwork::sendMessage(const QDataStream &msg, int msgid, quint32 receiver, quint32 sender)
447 { return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
448 
449 bool KGameNetwork::sendMessage(const QByteArray &msg, int msgid, quint32 receiver, quint32 sender)
450 { return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
451 
452 void KGameNetwork::sendError(int error,const QByteArray& message, quint32 receiver, quint32 sender)
453 {
454  QByteArray buffer;
455  QDataStream stream(&buffer,QIODevice::WriteOnly);
456  stream << (qint32) error;
457  stream.writeRawData(message.data(), message.size());
458  sendSystemMessage(stream,KGameMessage::IdError,receiver,sender);
459 }
460 
461 
462 // ----------------- receive messages from the network
463 void KGameNetwork::receiveNetworkTransmission(const QByteArray& receiveBuffer, quint32 clientID)
464 {
465  QDataStream stream(receiveBuffer);
466  int msgid;
467  quint32 sender; // the id of the KGame/KPlayer who sent the message
468  quint32 receiver; // the id of the KGame/KPlayer the message is for
469  KGameMessage::extractHeader(stream, sender, receiver, msgid);
470 // qCDebug(GAMES_PRIVATE_KGAME) << "id=" << msgid << "sender=" << sender << "recv=" << receiver;
471 
472  // No broadcast : receiver==0
473  // No player isPlayer(receiver)
474  // Different game gameId()!=receiver
475  if (receiver && receiver!=gameId() && !KGameMessage::isPlayer(receiver) )
476  {
477  // receiver=0 is broadcast or player message
478  qCDebug(GAMES_PRIVATE_KGAME) << "Message not meant for us "
479  << gameId() << "!=" << receiver << "rawid="
480  << KGameMessage::rawGameId(receiver);
481  return;
482  }
483  else if (msgid==KGameMessage::IdError)
484  {
485  QString text;
486  qint32 error;
487  stream >> error;
488  qCDebug(GAMES_PRIVATE_KGAME) << "Got IdError" << error;
489  text = KGameError::errorText(error, stream);
490  qCDebug(GAMES_PRIVATE_KGAME) << "Error text:" << text.toLatin1();
491  Q_EMIT signalNetworkErrorMessage((int)error,text);
492  }
493  else
494  {
495  networkTransmission(stream, msgid, receiver, sender, clientID);
496  }
497 }
498 
499 // -------------- slots for the signals of the client
501 {
503 
504 // TODO: I'm pretty sure there are a lot of things that should be done here...
505 }
506 
508 {
509  qCDebug(GAMES_PRIVATE_KGAME) << "------------------- KNETWORKGAME -------------------------";
510  qCDebug(GAMES_PRIVATE_KGAME) << "gameId " << gameId();
511  qCDebug(GAMES_PRIVATE_KGAME) << "gameMaster " << isMaster();
512  qCDebug(GAMES_PRIVATE_KGAME) << "gameAdmin " << isAdmin();
513  qCDebug(GAMES_PRIVATE_KGAME) << "---------------------------------------------------";
514 }
515 
516 /*
517  * vim: et sw=2
518  */
void aboutToLoseConnection(quint32 id)
Called when the network connection is about to terminate.
KMessageServer * messageServer() const
Don&#39;t use this unless you really know what you are doing! You might experience some strange behaviour...
bool sendMessage(const QByteArray &buffer, int msgid, quint32 receiver=0, quint32 sender=0)
Send a network message msg with a given message ID msgid to all clients.
bool isNetwork() const
void signalConnectionBroken()
Our connection to the KMessageServer has broken.
This class implements the message communication using a TCP/IP socket.
Definition: kmessageio.h:175
void sendError(int error, const QByteArray &message, quint32 receiver=0, quint32 sender=0)
Sends a network message.
void signalClientConnected(quint32 clientID)
This signal is emitted whenever the KMessageServer sends us a message that a new client connected...
QObject * sender() const const
void aboutToDisconnect(quint32 id)
This signal is emitted right before the client disconnects.
virtual int rtti() const
The runtime identification.
Definition: kmessageio.h:80
void forwardReceived(const QByteArray &msg, quint32 senderID, const QList< quint32 > &receivers)
This signal is emitted when the client receives a forward message from the KMessageServer, sent by another client.
void signalAdminStatusChanged(bool isAdmin)
This client gets or loses the admin status.
bool stopServerConnection()
Stops offering server connections - only for game MASTER.
int cookie() const
Application cookie.
virtual void lock()
You should call this before doing thigs like, e.g.
bool connectToServer(const QString &host, quint16 port)
Inits a network game as a network CLIENT.
A client to connect to a KMessageServer.
void adminStatusChanged(bool isAdmin)
This signal is emitted when this client becomes the admin client or when it loses the admin client st...
bool isMaster() const
Is this the game MASTER (i.e.
bool isEmpty() const const
This abstract base class represents one end of a message connections between two clients.
Definition: kmessageio.h:62
void electAdmin(quint32 clientID)
If you are the ADMIN of the game you can give the ADMIN status away to another client.
A server for message sending and broadcasting, using TCP/IP connections.
KMessageClient * messageClient() const
Don&#39;t use this unless you really know what you&#39;re doing! You might experience some strange behaviour ...
void eventClientConnected(quint32 clientID)
This signal is emitted when another client has connected to the server.
quint16 port() const
KGameNetwork(int cookie=42, QObject *parent=nullptr)
Create a KGameNetwork object.
bool isOfferingConnections() const
Are we still offer offering server connections - only for game MASTER.
QString hostName() const
QList::iterator end()
bool isAdmin() const
The admin of a game is the one who initializes newly connected clients using negotiateNetworkGame and...
void broadcastReceived(const QByteArray &msg, quint32 senderID)
This signal is emitted when the client receives a broadcast message from the KMessageServer, sent by another client.
bool offerConnections(quint16 port)
Inits a network game as network MASTER.
virtual void networkTransmission(QDataStream &, int, quint32, quint32, quint32 clientID)=0
Called by ReceiveNetworkTransmission().
void eventClientDisconnected(quint32 clientID, bool broken)
This signal is emitted when the server has lost the connection to one of the clients (This could be b...
virtual void unlock()
void setMaxClients(int max)
Changes the maximal connection number of the KMessageServer to max.
void lock()
Once this function is called no message will be received anymore.
QByteArray toLatin1() const const
void receiveNetworkTransmission(const QByteArray &a, quint32 clientID)
Called by KMessageClient::broadcastReceived() and will check if the message format is valid...
void connectionBroken()
This signal is emitted when the connection to the KMessageServer is broken.
void disconnect()
Disconnect the current connection and establish a new local one.
int writeRawData(const char *s, int len)
quint32 gameId() const
The unique ID of this game.
char * data()
void unlock()
Deliver every message that was delayed by lock() and actually deliver all messages that get received ...
void signalNetworkErrorMessage(int error, const QString &text)
A network error occurred.
void slotResetConnection()
Called when the network connection is terminated.
QIODevice * device() const const
virtual void Debug()
Gives debug output of the game status.
int size() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool sendSystemMessage(const QByteArray &buffer, int msgid, quint32 receiver=0, quint32 sender=0)
Sends a network message msg with a given msg id msgid to all clients.
void signalClientDisconnected(quint32 clientID, bool broken)
This signal is emitted whenever the KMessageServer sends us a message that a connection to a client w...
QList::iterator begin()
Q_EMITQ_EMIT
void slotAdminStatusChanged(bool isAdmin)
This KGame object receives or loses the admin status.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Nov 26 2020 22:36:18 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.