KDEGames

kgamenetwork.cpp
1 /*
2  This file is part of the KDE games library
3  SPDX-FileCopyrightText: 2001 Martin Heni <kde at heni-online.de>
4  SPDX-FileCopyrightText: 2001 Andreas Beckermann <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "kgamenetwork.h"
10 
11 // own
12 #include "kgamemessage.h"
13 #include "kgameerror.h"
14 #include "kmessageserver.h"
15 #include "kmessageclient.h"
16 #include "kmessageio.h"
17 // KF
18 #include <KDNSSD/PublicService>
19 // Qt
20 #include <QBuffer>
21 #include <QList>
22 
23 class KGameNetworkPrivate
24 {
25 public:
26  KGameNetworkPrivate()
27  {
28  mMessageClient = nullptr;
29  mMessageServer = nullptr;
30  mDisconnectId = 0;
31  mService = nullptr;
32  }
33 
34 public:
35  KMessageClient* mMessageClient;
36  KMessageServer* mMessageServer;
37  quint32 mDisconnectId; // Stores gameId() over a disconnect process
38  KDNSSD::PublicService* mService;
39  QString mType;
40  QString mName;
41 
42  int mCookie;
43 };
44 
45 // ------------------- NETWORK GAME ------------------------
47  : QObject(parent),
48  d( new KGameNetworkPrivate )
49 {
50  d->mCookie = (qint16)c;
51 
52  // Init the game as a local game, i.e.
53  // create your own KMessageServer and a KMessageClient connected to it.
54  setMaster();
55 
56  qCDebug(GAMES_PRIVATE_KGAME) << "this=" << this <<", cookie=" << cookie() << "sizeof(this)="<<sizeof(KGameNetwork);
57 }
58 
59 KGameNetwork::~KGameNetwork()
60 {
61  qCDebug(GAMES_PRIVATE_KGAME) << "this=" << this;
62 // Debug();
63  delete d->mService;
64 }
65 
66 // ----------------------------- status methods
68 { return isOfferingConnections() || d->mMessageClient->isNetwork();}
69 
70 quint32 KGameNetwork::gameId() const
71 {
72  //return d->mMessageClient->id() ;
73  // Return stored id in the case of disconnect. In any other
74  // case the disconnect id is 0
75  if (d->mMessageClient->id()!=0 ) {
76  return d->mMessageClient->id() ;
77  } else {
78  return d->mDisconnectId;
79  }
80 }
81 
83 { return d->mCookie; }
84 
86 { return (d->mMessageServer != nullptr); }
87 
89 { return (d->mMessageClient->isAdmin()); }
90 
92 { return d->mMessageClient; }
93 
95 { return d->mMessageServer; }
96 
97 // ----------------------- network init
99 {
100  if (!d->mMessageServer) {
101  d->mMessageServer = new KMessageServer (cookie(), this);
102  } else {
103  qCWarning(GAMES_PRIVATE_KGAME) << "Server already running!!";
104  }
105  if (!d->mMessageClient) {
106  d->mMessageClient = new KMessageClient (this);
111 
115 
116  // broacast and direct messages are treated equally on receive.
117  connect (d->mMessageClient, &KMessageClient::forwardReceived,
118  d->mMessageClient, &KMessageClient::broadcastReceived);
119 
120  } else {
121  // should be no problem but still has to be tested
122  qCDebug(GAMES_PRIVATE_KGAME) << "Client already exists!";
123  }
124  d->mMessageClient->setServer(d->mMessageServer);
125 }
126 
127 void KGameNetwork::setDiscoveryInfo(const QString& type, const QString& name)
128 {
129  qCDebug(GAMES_PRIVATE_KGAME) << type << ":" << name;
130  d->mType = type;
131  d->mName = name;
132  tryPublish();
133 }
134 
135 void KGameNetwork::tryPublish()
136 {
137  if (d->mType.isNull() || !isOfferingConnections()) return;
138  if (!d->mService) d->mService = new KDNSSD::PublicService(d->mName,d->mType,port());
139  else {
140  if (d->mType!=d->mService->type()) d->mService->setType(d->mType);
141  if (d->mName!=d->mService->serviceName()) d->mService->setServiceName(d->mName);
142  }
143  if (!d->mService->isPublished()) d->mService->publishAsync();
144 }
145 
146 void KGameNetwork::tryStopPublishing()
147 {
148  if (d->mService) d->mService->stop();
149 }
150 
152 {
153  qCDebug(GAMES_PRIVATE_KGAME) << "on port" << port;
154  if (!isMaster()) {
155  setMaster();
156  }
157 
158  // Make sure this is 0
159  d->mDisconnectId = 0;
160 
161  // FIXME: This debug message can be removed when the program is working correct.
162  if (d->mMessageServer && d->mMessageServer->isOfferingConnections()) {
163  qCDebug(GAMES_PRIVATE_KGAME) << "Already running as server! Changing the port now!";
164  }
165 
166  tryStopPublishing();
167  qCDebug(GAMES_PRIVATE_KGAME) << "before Server->initNetwork";
168  if (!d->mMessageServer->initNetwork (port)) {
169  qCCritical(GAMES_PRIVATE_KGAME) << "Unable to bind to port" << port << "!";
170  // no need to delete - we just cannot listen to the port
171 // delete d->mMessageServer;
172 // d->mMessageServer = 0;
173 // d->mMessageClient->setServer((KMessageServer*)0);
174  return false;
175  }
176  qCDebug(GAMES_PRIVATE_KGAME) << "after Server->initNetwork";
177  tryPublish();
178  return true;
179 }
180 
181 bool KGameNetwork::connectToServer (const QString& host, quint16 port)
182 {
183  if (host.isEmpty()) {
184  qCCritical(GAMES_PRIVATE_KGAME) << "No hostname given";
185  return false;
186  }
187  if (connectToServer(new KMessageSocket (host, port)))
188  {
189  qCDebug(GAMES_PRIVATE_KGAME) << "connected to" << host << ":" << port;
190  return true;
191  }
192  else
193  {
194  return false;
195  }
196 }
197 
199 {
200  // Make sure this is 0
201  d->mDisconnectId = 0;
202 
203  // if (!d->mMessageServer) {
204  // // FIXME: What shall we do here? Probably must stop a running game.
205  // qCWarning(GAMES_PRIVATE_KGAME) << "We are already connected to another server!";
206  /// }
207 
208  if (d->mMessageServer) {
209  // FIXME: What shall we do here? Probably must stop a running game.
210  qCWarning(GAMES_PRIVATE_KGAME) << "we are server but we are trying to connect to another server! "
211  << "make sure that all clients connect to that server! "
212  << "quitting the local server now...";
214  d->mMessageClient->setServer((KMessageIO*)nullptr);
215  delete d->mMessageServer;
216  d->mMessageServer = nullptr;
217  }
218 
219  qCDebug(GAMES_PRIVATE_KGAME) << " about to set server";
220  d->mMessageClient->setServer(connection);
221  Q_EMIT signalAdminStatusChanged(false); // as we delete the connection above isAdmin() is always false now!
222 
223  // OK: We say that we already have connected, but this isn't so yet!
224  // If the connection cannot be established, it will look as being disconnected
225  // again ("slotConnectionLost" is called).
226  // Shall we differ between these?
227  qCDebug(GAMES_PRIVATE_KGAME) << "connected";
228  return true;
229 }
230 
231 quint16 KGameNetwork::port() const
232 {
233  if (isNetwork()) {
234  if (isOfferingConnections()) {
235  return d->mMessageServer->serverPort();
236  } else {
237  return d->mMessageClient->peerPort();
238  }
239  }
240  return 0;
241 }
242 
244 {
245  return d->mMessageClient->peerName();
246 }
247 
249 {
250  // We still are the Master, we just don't accept further connections!
251  tryStopPublishing();
252  if (d->mMessageServer) {
253  d->mMessageServer->stopNetwork();
254  return true;
255  }
256  return false;
257 }
258 
260 { return (d->mMessageServer && d->mMessageServer->isOfferingConnections()); }
261 
263 {
264  // TODO MH
265  qCDebug(GAMES_PRIVATE_KGAME) ;
267  if (d->mMessageServer) {
268  QList <quint32> list=d->mMessageServer->clientIDs();
270  for( it = list.begin(); it != list.end(); ++it )
271  {
272  qCDebug(GAMES_PRIVATE_KGAME) << "Client id=" << (*it);
273  KMessageIO *client=d->mMessageServer->findClient(*it);
274  if (!client)
275  {
276  continue;
277  }
278  qCDebug(GAMES_PRIVATE_KGAME) << " rtti=" << client->rtti();
279  if (client->rtti()==2)
280  {
281  qCDebug(GAMES_PRIVATE_KGAME) << "DIRECT IO";
282  }
283  else
284  {
285  d->mMessageServer->removeClient(client,false);
286  }
287  }
288  }
289  else
290  {
291  qCDebug(GAMES_PRIVATE_KGAME) << "before client->disconnect() id="<<gameId();
292  //d->mMessageClient->setServer((KMessageIO*)0);
293  qCDebug(GAMES_PRIVATE_KGAME) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++";
294  d->mMessageClient->disconnect();
295 
296  qCDebug(GAMES_PRIVATE_KGAME) << "++++++--------------------------------------------+++++";
297  }
298  //setMaster();
299  /*
300  if (d->mMessageServer) {
301  //delete d->mMessageServer;
302  //d->mMessageServer=0;
303  server=true;
304  qCDebug(GAMES_PRIVATE_KGAME) << " server true";
305  d->mMessageServer->deleteClients();
306  qCDebug(GAMES_PRIVATE_KGAME) << " server deleteClients";
307  }
308  */
309  qCDebug(GAMES_PRIVATE_KGAME) << "DONE";
310 }
311 
313 {
314  qCDebug(GAMES_PRIVATE_KGAME) << "Storing client id of connection "<<clientID;
315  d->mDisconnectId = clientID;
316 }
317 
319 {
320  qCDebug(GAMES_PRIVATE_KGAME) << "Resseting client disconnect id";
321  d->mDisconnectId = 0;
322 }
323 
324 void KGameNetwork::electAdmin(quint32 clientID)
325 {
326  if (!isAdmin()) {
327  qCWarning(GAMES_PRIVATE_KGAME) << "only ADMIN is allowed to call this!";
328  return;
329  }
330  QByteArray buffer;
331  QDataStream stream(&buffer,QIODevice::WriteOnly);
332  stream << static_cast<quint32>( KMessageServer::REQ_ADMIN_CHANGE );
333  stream << clientID;
334  d->mMessageClient->sendServerMessage(buffer);
335 }
336 
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_MAX_NUM_CLIENTS );
346  stream << (qint32)max;
347  d->mMessageClient->sendServerMessage(buffer);
348 }
349 
351 {
352  if (messageClient()) {
353  messageClient()->lock();
354  }
355 }
356 
358 {
359  if (messageClient()) {
360  messageClient()->unlock();
361  }
362 }
363 
364 // --------------------- send messages ---------------------------
365 
366 bool KGameNetwork::sendSystemMessage(int data, int msgid, quint32 receiver, quint32 sender)
367 {
368  QByteArray buffer;
369  QDataStream stream(&buffer,QIODevice::WriteOnly);
370  stream << data;
371  return sendSystemMessage(buffer,msgid,receiver,sender);
372 }
373 
374 bool KGameNetwork::sendSystemMessage(const QString &msg, int msgid, quint32 receiver, quint32 sender)
375 {
376  QByteArray buffer;
377  QDataStream stream(&buffer, QIODevice::WriteOnly);
378  stream << msg;
379  return sendSystemMessage(buffer, msgid, receiver, sender);
380 }
381 
382 bool KGameNetwork::sendSystemMessage(const QDataStream &msg, int msgid, quint32 receiver, quint32 sender)
383 { return sendSystemMessage(((QBuffer*)msg.device())->buffer(), msgid, receiver, sender); }
384 
385 bool KGameNetwork::sendSystemMessage(const QByteArray& data, int msgid, quint32 receiver, quint32 sender)
386 {
387  QByteArray buffer;
388  QDataStream stream(&buffer,QIODevice::WriteOnly);
389  if (!sender) {
390  sender = gameId();
391  }
392 
393  quint32 receiverClient = KGameMessage::rawGameId(receiver); // KGame::gameId()
394  int receiverPlayer = KGameMessage::rawPlayerId(receiver); // KPlayer::id()
395 
396  KGameMessage::createHeader(stream, sender, receiver, msgid);
397  stream.writeRawData(data.data(), data.size());
398 
399  /*
400  qCDebug(GAMES_PRIVATE_KGAME) << "transmitGameClientMessage msgid=" << msgid << "recv="
401  << receiver << "sender=" << sender << "Buffersize="
402  << buffer.size();
403  */
404 
405  if (!d->mMessageClient) {
406  // No client created, this should never happen!
407  // Having a local game means we have our own
408  // KMessageServer and we are the only client.
409  qCWarning(GAMES_PRIVATE_KGAME) << "We don't have a client! Should never happen!";
410  return false;
411  }
412 
413  if (receiverClient == 0 || receiverPlayer != 0)
414  {
415  // if receiverClient == 0 this is a broadcast message. if it is != 0 but
416  // receiverPlayer is also != 0 we have to send broadcast anyway, because the
417  // KPlayer object on all clients needs to receive the message.
418  d->mMessageClient->sendBroadcast(buffer);
419  }
420  else
421  {
422  d->mMessageClient->sendForward(buffer, receiverClient);
423  }
424  return true;
425 }
426 
427 bool KGameNetwork::sendMessage(int data, int msgid, quint32 receiver, quint32 sender)
428 { return sendSystemMessage(data,msgid+KGameMessage::IdUser,receiver,sender); }
429 
430 bool KGameNetwork::sendMessage(const QString &msg, int msgid, quint32 receiver, quint32 sender)
431 { return sendSystemMessage(msg,msgid+KGameMessage::IdUser,receiver,sender); }
432 
433 bool KGameNetwork::sendMessage(const QDataStream &msg, int msgid, quint32 receiver, quint32 sender)
434 { return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
435 
436 bool KGameNetwork::sendMessage(const QByteArray &msg, int msgid, quint32 receiver, quint32 sender)
437 { return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
438 
439 void KGameNetwork::sendError(int error,const QByteArray& message, quint32 receiver, quint32 sender)
440 {
441  QByteArray buffer;
442  QDataStream stream(&buffer,QIODevice::WriteOnly);
443  stream << (qint32) error;
444  stream.writeRawData(message.data(), message.size());
445  sendSystemMessage(stream,KGameMessage::IdError,receiver,sender);
446 }
447 
448 
449 // ----------------- receive messages from the network
450 void KGameNetwork::receiveNetworkTransmission(const QByteArray& receiveBuffer, quint32 clientID)
451 {
452  QDataStream stream(receiveBuffer);
453  int msgid;
454  quint32 sender; // the id of the KGame/KPlayer who sent the message
455  quint32 receiver; // the id of the KGame/KPlayer the message is for
456  KGameMessage::extractHeader(stream, sender, receiver, msgid);
457 // qCDebug(GAMES_PRIVATE_KGAME) << "id=" << msgid << "sender=" << sender << "recv=" << receiver;
458 
459  // No broadcast : receiver==0
460  // No player isPlayer(receiver)
461  // Different game gameId()!=receiver
462  if (receiver && receiver!=gameId() && !KGameMessage::isPlayer(receiver) )
463  {
464  // receiver=0 is broadcast or player message
465  qCDebug(GAMES_PRIVATE_KGAME) << "Message not meant for us "
466  << gameId() << "!=" << receiver << "rawid="
467  << KGameMessage::rawGameId(receiver);
468  return;
469  }
470  else if (msgid==KGameMessage::IdError)
471  {
472  QString text;
473  qint32 error;
474  stream >> error;
475  qCDebug(GAMES_PRIVATE_KGAME) << "Got IdError" << error;
476  text = KGameError::errorText(error, stream);
477  qCDebug(GAMES_PRIVATE_KGAME) << "Error text:" << text.toLatin1();
478  Q_EMIT signalNetworkErrorMessage((int)error,text);
479  }
480  else
481  {
482  networkTransmission(stream, msgid, receiver, sender, clientID);
483  }
484 }
485 
486 // -------------- slots for the signals of the client
488 {
490 
491 // TODO: I'm pretty sure there are a lot of things that should be done here...
492 }
493 
495 {
496  qCDebug(GAMES_PRIVATE_KGAME) << "------------------- KNETWORKGAME -------------------------";
497  qCDebug(GAMES_PRIVATE_KGAME) << "gameId " << gameId();
498  qCDebug(GAMES_PRIVATE_KGAME) << "gameMaster " << isMaster();
499  qCDebug(GAMES_PRIVATE_KGAME) << "gameAdmin " << isAdmin();
500  qCDebug(GAMES_PRIVATE_KGAME) << "---------------------------------------------------";
501 }
502 
503 /*
504  * vim: et sw=2
505  */
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:164
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:69
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.
const QLatin1String name
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.
PartitionTable::TableType type
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:51
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 setMaster()
void slotAdminStatusChanged(bool isAdmin)
This KGame object receives or loses the admin status.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Dec 7 2021 22:34:14 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.