File indexing completed on 2024-06-16 03:57:55
0001 /* 0002 This file is part of the KDE games library 0003 SPDX-FileCopyrightText: 2001 Martin Heni <kde at heni-online.de> 0004 SPDX-FileCopyrightText: 2001 Andreas Beckermann <b_mann@gmx.de> 0005 0006 SPDX-License-Identifier: LGPL-2.0-only 0007 */ 0008 0009 #include "kgamenetwork.h" 0010 0011 // own 0012 #include "kgameerror.h" 0013 #include "kgamemessage.h" 0014 #include "kmessageclient.h" 0015 #include "kmessageio.h" 0016 #include "kmessageserver.h" 0017 #include <kdegamesprivate_kgame_logging.h> 0018 // KF 0019 #include <KDNSSD/PublicService> 0020 // Qt 0021 #include <QBuffer> 0022 #include <QList> 0023 0024 class KGameNetworkPrivate 0025 { 0026 public: 0027 KGameNetworkPrivate() = default; 0028 0029 public: 0030 KMessageClient *mMessageClient = nullptr; 0031 KMessageServer *mMessageServer = nullptr; 0032 quint32 mDisconnectId = 0; // Stores gameId() over a disconnect process 0033 KDNSSD::PublicService *mService = nullptr; 0034 QString mType; 0035 QString mName; 0036 0037 int mCookie; 0038 }; 0039 0040 // ------------------- NETWORK GAME ------------------------ 0041 KGameNetwork::KGameNetwork(int c, QObject *parent) 0042 : QObject(parent) 0043 , d(new KGameNetworkPrivate) 0044 { 0045 d->mCookie = (qint16)c; 0046 0047 // Init the game as a local game, i.e. 0048 // create your own KMessageServer and a KMessageClient connected to it. 0049 setMaster(); 0050 0051 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "this=" << this << ", cookie=" << cookie() << "sizeof(this)=" << sizeof(KGameNetwork); 0052 } 0053 0054 KGameNetwork::~KGameNetwork() 0055 { 0056 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "this=" << this; 0057 // Debug(); 0058 delete d->mService; 0059 } 0060 0061 // ----------------------------- status methods 0062 bool KGameNetwork::isNetwork() const 0063 { 0064 return isOfferingConnections() || d->mMessageClient->isNetwork(); 0065 } 0066 0067 quint32 KGameNetwork::gameId() const 0068 { 0069 // return d->mMessageClient->id() ; 0070 // Return stored id in the case of disconnect. In any other 0071 // case the disconnect id is 0 0072 if (d->mMessageClient->id() != 0) { 0073 return d->mMessageClient->id(); 0074 } else { 0075 return d->mDisconnectId; 0076 } 0077 } 0078 0079 int KGameNetwork::cookie() const 0080 { 0081 return d->mCookie; 0082 } 0083 0084 bool KGameNetwork::isMaster() const 0085 { 0086 return (d->mMessageServer != nullptr); 0087 } 0088 0089 bool KGameNetwork::isAdmin() const 0090 { 0091 return (d->mMessageClient->isAdmin()); 0092 } 0093 0094 KMessageClient *KGameNetwork::messageClient() const 0095 { 0096 return d->mMessageClient; 0097 } 0098 0099 KMessageServer *KGameNetwork::messageServer() const 0100 { 0101 return d->mMessageServer; 0102 } 0103 0104 // ----------------------- network init 0105 void KGameNetwork::setMaster() 0106 { 0107 if (!d->mMessageServer) { 0108 d->mMessageServer = new KMessageServer(cookie(), this); 0109 } else { 0110 qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << "Server already running!!"; 0111 } 0112 if (!d->mMessageClient) { 0113 d->mMessageClient = new KMessageClient(this); 0114 connect(d->mMessageClient, &KMessageClient::broadcastReceived, this, &KGameNetwork::receiveNetworkTransmission); 0115 connect(d->mMessageClient, &KMessageClient::connectionBroken, this, &KGameNetwork::signalConnectionBroken); 0116 connect(d->mMessageClient, &KMessageClient::aboutToDisconnect, this, &KGameNetwork::aboutToLoseConnection); 0117 connect(d->mMessageClient, &KMessageClient::connectionBroken, this, &KGameNetwork::slotResetConnection); 0118 0119 connect(d->mMessageClient, &KMessageClient::adminStatusChanged, this, &KGameNetwork::slotAdminStatusChanged); 0120 connect(d->mMessageClient, &KMessageClient::eventClientConnected, this, &KGameNetwork::signalClientConnected); 0121 connect(d->mMessageClient, &KMessageClient::eventClientDisconnected, this, &KGameNetwork::signalClientDisconnected); 0122 0123 // broacast and direct messages are treated equally on receive. 0124 connect(d->mMessageClient, &KMessageClient::forwardReceived, d->mMessageClient, &KMessageClient::broadcastReceived); 0125 0126 } else { 0127 // should be no problem but still has to be tested 0128 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Client already exists!"; 0129 } 0130 d->mMessageClient->setServer(d->mMessageServer); 0131 } 0132 0133 void KGameNetwork::setDiscoveryInfo(const QString &type, const QString &name) 0134 { 0135 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << type << ":" << name; 0136 d->mType = type; 0137 d->mName = name; 0138 tryPublish(); 0139 } 0140 0141 void KGameNetwork::tryPublish() 0142 { 0143 if (d->mType.isEmpty() || !isOfferingConnections()) 0144 return; 0145 if (!d->mService) 0146 d->mService = new KDNSSD::PublicService(d->mName, d->mType, port()); 0147 else { 0148 if (d->mType != d->mService->type()) 0149 d->mService->setType(d->mType); 0150 if (d->mName != d->mService->serviceName()) 0151 d->mService->setServiceName(d->mName); 0152 } 0153 if (!d->mService->isPublished()) 0154 d->mService->publishAsync(); 0155 } 0156 0157 void KGameNetwork::tryStopPublishing() 0158 { 0159 if (d->mService) 0160 d->mService->stop(); 0161 } 0162 0163 bool KGameNetwork::offerConnections(quint16 port) 0164 { 0165 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "on port" << port; 0166 if (!isMaster()) { 0167 setMaster(); 0168 } 0169 0170 // Make sure this is 0 0171 d->mDisconnectId = 0; 0172 0173 // FIXME: This debug message can be removed when the program is working correct. 0174 if (d->mMessageServer && d->mMessageServer->isOfferingConnections()) { 0175 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Already running as server! Changing the port now!"; 0176 } 0177 0178 tryStopPublishing(); 0179 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "before Server->initNetwork"; 0180 if (!d->mMessageServer->initNetwork(port)) { 0181 qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << "Unable to bind to port" << port << "!"; 0182 // no need to delete - we just cannot listen to the port 0183 // delete d->mMessageServer; 0184 // d->mMessageServer = 0; 0185 // d->mMessageClient->setServer((KMessageServer*)0); 0186 return false; 0187 } 0188 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "after Server->initNetwork"; 0189 tryPublish(); 0190 return true; 0191 } 0192 0193 bool KGameNetwork::connectToServer(const QString &host, quint16 port) 0194 { 0195 if (host.isEmpty()) { 0196 qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << "No hostname given"; 0197 return false; 0198 } 0199 if (connectToServer(new KMessageSocket(host, port))) { 0200 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "connected to" << host << ":" << port; 0201 return true; 0202 } else { 0203 return false; 0204 } 0205 } 0206 0207 bool KGameNetwork::connectToServer(KMessageIO *connection) 0208 { 0209 // Make sure this is 0 0210 d->mDisconnectId = 0; 0211 0212 // if (!d->mMessageServer) { 0213 // // FIXME: What shall we do here? Probably must stop a running game. 0214 // qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << "We are already connected to another server!"; 0215 /// } 0216 0217 if (d->mMessageServer) { 0218 // FIXME: What shall we do here? Probably must stop a running game. 0219 qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << "we are server but we are trying to connect to another server! " 0220 << "make sure that all clients connect to that server! " 0221 << "quitting the local server now..."; 0222 stopServerConnection(); 0223 d->mMessageClient->setServer((KMessageIO *)nullptr); 0224 delete d->mMessageServer; 0225 d->mMessageServer = nullptr; 0226 } 0227 0228 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << " about to set server"; 0229 d->mMessageClient->setServer(connection); 0230 Q_EMIT signalAdminStatusChanged(false); // as we delete the connection above isAdmin() is always false now! 0231 0232 // OK: We say that we already have connected, but this isn't so yet! 0233 // If the connection cannot be established, it will look as being disconnected 0234 // again ("slotConnectionLost" is called). 0235 // Shall we differ between these? 0236 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "connected"; 0237 return true; 0238 } 0239 0240 quint16 KGameNetwork::port() const 0241 { 0242 if (isNetwork()) { 0243 if (isOfferingConnections()) { 0244 return d->mMessageServer->serverPort(); 0245 } else { 0246 return d->mMessageClient->peerPort(); 0247 } 0248 } 0249 return 0; 0250 } 0251 0252 QString KGameNetwork::hostName() const 0253 { 0254 return d->mMessageClient->peerName(); 0255 } 0256 0257 bool KGameNetwork::stopServerConnection() 0258 { 0259 // We still are the Master, we just don't accept further connections! 0260 tryStopPublishing(); 0261 if (d->mMessageServer) { 0262 d->mMessageServer->stopNetwork(); 0263 return true; 0264 } 0265 return false; 0266 } 0267 0268 bool KGameNetwork::isOfferingConnections() const 0269 { 0270 return (d->mMessageServer && d->mMessageServer->isOfferingConnections()); 0271 } 0272 0273 void KGameNetwork::disconnect() 0274 { 0275 // TODO MH 0276 qCDebug(KDEGAMESPRIVATE_KGAME_LOG); 0277 stopServerConnection(); 0278 if (d->mMessageServer) { 0279 const QList<quint32> list = d->mMessageServer->clientIDs(); 0280 for (quint32 id : list) { 0281 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Client id=" << id; 0282 KMessageIO *client = d->mMessageServer->findClient(id); 0283 if (!client) { 0284 continue; 0285 } 0286 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << " rtti=" << client->rtti(); 0287 if (client->rtti() == 2) { 0288 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "DIRECT IO"; 0289 } else { 0290 d->mMessageServer->removeClient(client, false); 0291 } 0292 } 0293 } else { 0294 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "before client->disconnect() id=" << gameId(); 0295 // d->mMessageClient->setServer((KMessageIO*)0); 0296 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++"; 0297 d->mMessageClient->disconnect(); 0298 0299 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "++++++--------------------------------------------+++++"; 0300 } 0301 // setMaster(); 0302 /* 0303 if (d->mMessageServer) { 0304 //delete d->mMessageServer; 0305 //d->mMessageServer=0; 0306 server=true; 0307 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << " server true"; 0308 d->mMessageServer->deleteClients(); 0309 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << " server deleteClients"; 0310 } 0311 */ 0312 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "DONE"; 0313 } 0314 0315 void KGameNetwork::aboutToLoseConnection(quint32 clientID) 0316 { 0317 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Storing client id of connection " << clientID; 0318 d->mDisconnectId = clientID; 0319 } 0320 0321 void KGameNetwork::slotResetConnection() 0322 { 0323 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Resseting client disconnect id"; 0324 d->mDisconnectId = 0; 0325 } 0326 0327 void KGameNetwork::electAdmin(quint32 clientID) 0328 { 0329 if (!isAdmin()) { 0330 qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << "only ADMIN is allowed to call this!"; 0331 return; 0332 } 0333 QByteArray buffer; 0334 QDataStream stream(&buffer, QIODevice::WriteOnly); 0335 stream << static_cast<quint32>(KMessageServer::REQ_ADMIN_CHANGE); 0336 stream << clientID; 0337 d->mMessageClient->sendServerMessage(buffer); 0338 } 0339 0340 void KGameNetwork::setMaxClients(int max) 0341 { 0342 if (!isAdmin()) { 0343 qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << "only ADMIN is allowed to call this!"; 0344 return; 0345 } 0346 QByteArray buffer; 0347 QDataStream stream(&buffer, QIODevice::WriteOnly); 0348 stream << static_cast<quint32>(KMessageServer::REQ_MAX_NUM_CLIENTS); 0349 stream << (qint32)max; 0350 d->mMessageClient->sendServerMessage(buffer); 0351 } 0352 0353 void KGameNetwork::lock() 0354 { 0355 if (messageClient()) { 0356 messageClient()->lock(); 0357 } 0358 } 0359 0360 void KGameNetwork::unlock() 0361 { 0362 if (messageClient()) { 0363 messageClient()->unlock(); 0364 } 0365 } 0366 0367 // --------------------- send messages --------------------------- 0368 0369 bool KGameNetwork::sendSystemMessage(int data, int msgid, quint32 receiver, quint32 sender) 0370 { 0371 QByteArray buffer; 0372 QDataStream stream(&buffer, QIODevice::WriteOnly); 0373 stream << data; 0374 return sendSystemMessage(buffer, msgid, receiver, sender); 0375 } 0376 0377 bool KGameNetwork::sendSystemMessage(const QString &msg, int msgid, quint32 receiver, quint32 sender) 0378 { 0379 QByteArray buffer; 0380 QDataStream stream(&buffer, QIODevice::WriteOnly); 0381 stream << msg; 0382 return sendSystemMessage(buffer, msgid, receiver, sender); 0383 } 0384 0385 bool KGameNetwork::sendSystemMessage(const QDataStream &msg, int msgid, quint32 receiver, quint32 sender) 0386 { 0387 return sendSystemMessage(((QBuffer *)msg.device())->buffer(), msgid, receiver, sender); 0388 } 0389 0390 bool KGameNetwork::sendSystemMessage(const QByteArray &data, int msgid, quint32 receiver, quint32 sender) 0391 { 0392 QByteArray buffer; 0393 QDataStream stream(&buffer, QIODevice::WriteOnly); 0394 if (!sender) { 0395 sender = gameId(); 0396 } 0397 0398 quint32 receiverClient = KGameMessage::rawGameId(receiver); // KGame::gameId() 0399 int receiverPlayer = KGameMessage::rawPlayerId(receiver); // KPlayer::id() 0400 0401 KGameMessage::createHeader(stream, sender, receiver, msgid); 0402 stream.writeRawData(data.data(), data.size()); 0403 0404 /* 0405 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "transmitGameClientMessage msgid=" << msgid << "recv=" 0406 << receiver << "sender=" << sender << "Buffersize=" 0407 << buffer.size(); 0408 */ 0409 0410 if (!d->mMessageClient) { 0411 // No client created, this should never happen! 0412 // Having a local game means we have our own 0413 // KMessageServer and we are the only client. 0414 qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << "We don't have a client! Should never happen!"; 0415 return false; 0416 } 0417 0418 if (receiverClient == 0 || receiverPlayer != 0) { 0419 // if receiverClient == 0 this is a broadcast message. if it is != 0 but 0420 // receiverPlayer is also != 0 we have to send broadcast anyway, because the 0421 // KPlayer object on all clients needs to receive the message. 0422 d->mMessageClient->sendBroadcast(buffer); 0423 } else { 0424 d->mMessageClient->sendForward(buffer, receiverClient); 0425 } 0426 return true; 0427 } 0428 0429 bool KGameNetwork::sendMessage(int data, int msgid, quint32 receiver, quint32 sender) 0430 { 0431 return sendSystemMessage(data, msgid + KGameMessage::IdUser, receiver, sender); 0432 } 0433 0434 bool KGameNetwork::sendMessage(const QString &msg, int msgid, quint32 receiver, quint32 sender) 0435 { 0436 return sendSystemMessage(msg, msgid + KGameMessage::IdUser, receiver, sender); 0437 } 0438 0439 bool KGameNetwork::sendMessage(const QDataStream &msg, int msgid, quint32 receiver, quint32 sender) 0440 { 0441 return sendSystemMessage(msg, msgid + KGameMessage::IdUser, receiver, sender); 0442 } 0443 0444 bool KGameNetwork::sendMessage(const QByteArray &msg, int msgid, quint32 receiver, quint32 sender) 0445 { 0446 return sendSystemMessage(msg, msgid + KGameMessage::IdUser, receiver, sender); 0447 } 0448 0449 void KGameNetwork::sendError(int error, const QByteArray &message, quint32 receiver, quint32 sender) 0450 { 0451 QByteArray buffer; 0452 QDataStream stream(&buffer, QIODevice::WriteOnly); 0453 stream << (qint32)error; 0454 stream.writeRawData(message.data(), message.size()); 0455 sendSystemMessage(stream, KGameMessage::IdError, receiver, sender); 0456 } 0457 0458 // ----------------- receive messages from the network 0459 void KGameNetwork::receiveNetworkTransmission(const QByteArray &receiveBuffer, quint32 clientID) 0460 { 0461 QDataStream stream(receiveBuffer); 0462 int msgid; 0463 quint32 sender; // the id of the KGame/KPlayer who sent the message 0464 quint32 receiver; // the id of the KGame/KPlayer the message is for 0465 KGameMessage::extractHeader(stream, sender, receiver, msgid); 0466 // qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "id=" << msgid << "sender=" << sender << "recv=" << receiver; 0467 0468 // No broadcast : receiver==0 0469 // No player isPlayer(receiver) 0470 // Different game gameId()!=receiver 0471 if (receiver && receiver != gameId() && !KGameMessage::isPlayer(receiver)) { 0472 // receiver=0 is broadcast or player message 0473 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Message not meant for us " << gameId() << "!=" << receiver << "rawid=" << KGameMessage::rawGameId(receiver); 0474 return; 0475 } else if (msgid == KGameMessage::IdError) { 0476 QString text; 0477 qint32 error; 0478 stream >> error; 0479 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Got IdError" << error; 0480 text = KGameError::errorText(error, stream); 0481 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Error text:" << text.toLatin1(); 0482 Q_EMIT signalNetworkErrorMessage((int)error, text); 0483 } else { 0484 networkTransmission(stream, msgid, receiver, sender, clientID); 0485 } 0486 } 0487 0488 // -------------- slots for the signals of the client 0489 void KGameNetwork::slotAdminStatusChanged(bool isAdmin) 0490 { 0491 Q_EMIT signalAdminStatusChanged(isAdmin); 0492 0493 // TODO: I'm pretty sure there are a lot of things that should be done here... 0494 } 0495 0496 void KGameNetwork::Debug() 0497 { 0498 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "------------------- KNETWORKGAME -------------------------"; 0499 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "gameId " << gameId(); 0500 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "gameMaster " << isMaster(); 0501 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "gameAdmin " << isAdmin(); 0502 qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "---------------------------------------------------"; 0503 } 0504 0505 #include "moc_kgamenetwork.cpp" 0506 0507 /* 0508 * vim: et sw=2 0509 */