File indexing completed on 2024-04-14 03:59:24

0001 /***************************************************************************
0002 *   KBlocks, a falling blocks game by KDE                                *
0003 *   SPDX-FileCopyrightText: 2010 Zhongjie Cai <squall.leonhart.cai@gmail.com>       *
0004 *                                                                         *
0005 *   SPDX-License-Identifier: GPL-2.0-or-later
0006 ***************************************************************************/
0007 #include "KBlocksNetServer.h"
0008 #include "GamePlayerInterface.h"
0009 #include "KBlocksDefine.h"
0010 #include "KBlocksSingleGame.h"
0011 #include "KBlocksField.h"
0012 #include "KBlocksPiece.h"
0013 
0014 #include <QVarLengthArray>
0015 
0016 KBlocksNetServer::KBlocksNetServer(KBlocksGameLogic *p, const QString &localIP)
0017 {
0018     mpGameLogic = p;
0019     maGameScoreList = nullptr;
0020 
0021     mGameCount = 0;
0022     mGameStarted = false;
0023     mWaitForAll = false;
0024     mTopGameLevel = -1;
0025     mInitSendLength = 0;
0026     mLvUpSendLength = 0;
0027 
0028     parseIPString(localIP, &mLocalAddress, &mLocalPort);
0029     // mRemoteAddress and mRemotePort are only used in recvRemoteData
0030     // and will be set there.
0031     mRemotePort = 0;
0032 
0033     mpServerSocket = new QUdpSocket(this);
0034     mpServerSocket->bind(mLocalAddress, mLocalPort);
0035 
0036     mRunningFlag = false;
0037 
0038     mRecordFileName = string("");
0039     mRecordFileType = true;
0040 }
0041 
0042 KBlocksNetServer::~KBlocksNetServer()
0043 {
0044     delete mpServerSocket;
0045 }
0046 
0047 int KBlocksNetServer::executeGame(int gameCount, bool waitForAll)
0048 {
0049     mGameCount = gameCount;
0050     mWaitForAll = waitForAll;
0051     maGameScoreList = new KBlocksScore*[mGameCount];
0052     mTopGameLevel = -1;
0053     for (int i = 0; i < mGameCount; i++) {
0054         maGameScoreList[i] = new KBlocksScore();
0055         maGameScoreList[i]->setLevelUpFactor(KBlocksScore_Level_x_Level_x_Factor, 1000);
0056         maGameScoreList[i]->setScoreUpFactor(10);
0057     }
0058 
0059     mRunningFlag = true;
0060 
0061     QList<QByteArray> tmpRecvData;
0062     QList<QString> tmpRecvAddr;
0063     int recvResult = 0;
0064     while (mRunningFlag) {
0065         recvRemoteData(&tmpRecvData, &tmpRecvAddr);
0066 
0067         if (tmpRecvData.isEmpty()) {
0068             recvResult = processGame(-1);
0069         } else {
0070             recvResult = 0;
0071             for (int i = 0; i < tmpRecvData.size(); ++i) {
0072                 int result = parseRemoteData(tmpRecvData[i], tmpRecvAddr[i]);
0073                 if (result != -1) {
0074                     processGame(result);
0075                 } else {
0076                     recvResult = 1;
0077                 }
0078             }
0079 
0080             if ((!mWaitForAll) && (recvResult == 0)) {
0081                 recvResult = processGame(-1);
0082             }
0083         }
0084 
0085         if ((recvResult < -1) && mGameStarted) {
0086             break;
0087         }
0088     }
0089 
0090     mRunningFlag = false;
0091 
0092     if (!mRecordFileName.empty()) {
0093         mpGameLogic->saveRecord(mRecordFileName.c_str(), mRecordFileType);
0094     }
0095 
0096     sendGameOver();
0097 
0098     printGameResult();
0099 
0100     for (int i = 0; i < mGameCount; i++) {
0101         delete maGameScoreList[i];
0102     }
0103     delete [] maGameScoreList;
0104 
0105     if (recvResult != -2) {
0106         return 0;
0107     }
0108 
0109     return recvResult;
0110 }
0111 
0112 void KBlocksNetServer::setSendLength(int initLen, int lvUpLen)
0113 {
0114     mInitSendLength = initLen;
0115     mLvUpSendLength = lvUpLen;
0116 }
0117 
0118 void KBlocksNetServer::setRecordFile(const char *fileName, bool binaryMode)
0119 {
0120     mRecordFileName = string(fileName);
0121     mRecordFileType = binaryMode;
0122 }
0123 
0124 void KBlocksNetServer::recvRemoteData(QList<QByteArray> *recvData, QList<QString> *recvAddr)
0125 {
0126     if (!mRunningFlag) {
0127         return;
0128     }
0129 
0130     recvData->clear();
0131     recvAddr->clear();
0132 
0133     while (mpServerSocket->hasPendingDatagrams()) {
0134         QByteArray tmpData;
0135         tmpData.resize(mpServerSocket->pendingDatagramSize());
0136 
0137         mpServerSocket->readDatagram(tmpData.data(), tmpData.size(), &mRemoteAddress, &mRemotePort);
0138         QString tmpAddr = formIPString(mRemoteAddress, mRemotePort);
0139 
0140         recvData->append(tmpData);
0141         recvAddr->append(tmpAddr);
0142 
0143         //printf(" Recv From %s : %s\n", tmpAddr.toStdString().c_str(), tmpData.data());
0144         //printf("   [%s:%d]\n", mRemoteAddress.toString().toStdString().c_str(), mRemotePort);
0145     }
0146 }
0147 
0148 int KBlocksNetServer::processGame(int gameIndex)
0149 {
0150     if (gameIndex >= 0) {
0151         //printf("\tStepping game (%d)...\n", gameIndex);
0152         mpGameLogic->getSingleGame(gameIndex)->updateGame();
0153         //printf("\tSending new play data (%d)...\n", gameIndex);
0154         sendPlayerData(gameIndex);
0155         return gameIndex;
0156     } else {
0157         QVarLengthArray<int, 16> removedLines(mGameCount);
0158         int activeCount = mpGameLogic->getActiveGameCount();
0159         if ((activeCount > 1) || (mGameCount == activeCount)) {
0160             mpGameLogic->updateGame(removedLines.data());
0161             for (int i = 0; i < mGameCount; i++) {
0162                 if (maGameScoreList[i]->addScore(removedLines[i])) {
0163                     int tmpLevel = maGameScoreList[i]->getGameLevel();
0164                     if (mTopGameLevel < tmpLevel) {
0165                         mpGameLogic->levelUpGame(tmpLevel - mTopGameLevel);
0166                         mTopGameLevel = tmpLevel;
0167                         sendPlayerActionLength();
0168                     }
0169                 }
0170             }
0171             if (mWaitForAll) {
0172                 bool allWait = true;
0173                 for (int i = 0; i < mGameCount; i++) {
0174                     if (mpGameLogic->getSingleGame(i)->isActive()) {
0175                         allWait = false;
0176                         break;
0177                     }
0178                 }
0179                 if (allWait) {
0180                     mpGameLogic->continueGame();
0181                 }
0182             }
0183             return -1;
0184         } else {
0185             return -2;
0186         }
0187     }
0188 }
0189 
0190 void KBlocksNetServer::addPlayerIP(int gameIndex, const QByteArray &data, const QString &addr)
0191 {
0192     QString playerName = QString::fromUtf8(data).mid(6);
0193     if (mPlayerMapping.find(addr) == mPlayerMapping.end()) {
0194         mPlayerIPList.append(addr);
0195     }
0196     mPlayerMapping[addr] = gameIndex;
0197     mPlayerName[gameIndex] = playerName;
0198 }
0199 
0200 void KBlocksNetServer::delPlayerIP(const QString &addr)
0201 {
0202     if (mPlayerMapping.find(addr) != mPlayerMapping.end()) {
0203         mPlayerMapping.remove(addr);
0204         mPlayerIPList.removeOne(addr);
0205     }
0206 }
0207 
0208 void KBlocksNetServer::sendPlayerActionLength()
0209 {
0210     QHostAddress tmpIP;
0211     quint16 tmpPort;
0212     QByteArray tmpByteData;
0213 
0214     int tmpLength = mInitSendLength - mTopGameLevel * mLvUpSendLength;
0215     if (mInitSendLength <= 0) {
0216         return;
0217     } else {
0218         if (tmpLength < 1) {
0219             tmpLength = 1;
0220         } else if (tmpLength > 255) {
0221             tmpLength = 255;
0222         }
0223     }
0224 
0225     QList<QString>::iterator it;
0226     for (it = mPlayerIPList.begin(); it != mPlayerIPList.end(); ++it) {
0227         parseIPString(*it, &tmpIP, &tmpPort);
0228 
0229         tmpByteData.append((char) - 1);
0230         tmpByteData.append((char)tmpLength);
0231         tmpByteData.append((char)0);
0232 
0233         mpServerSocket->writeDatagram(tmpByteData, tmpIP, tmpPort);
0234     }
0235 }
0236 
0237 void KBlocksNetServer::sendPlayerData(int gameIndex)
0238 {
0239     char tmpData[256];
0240     int tmpByteCount;
0241     QByteArray tmpByteData;
0242 
0243     QList<QString>::iterator it;
0244     for (it = mPlayerIPList.begin(); it != mPlayerIPList.end(); ++it) {
0245         int gameID = mPlayerMapping[*it];
0246 
0247         if (gameIndex == gameID) {
0248             tmpByteData.clear();
0249 
0250             tmpByteData.append((char)gameID);
0251 
0252             formByteFromInt(maGameScoreList[gameID]->getScorePoint(), tmpData + 0);
0253             formByteFromInt(maGameScoreList[gameID]->getLineCount(),  tmpData + 4);
0254             formByteFromInt(maGameScoreList[gameID]->getGameLevel(),  tmpData + 8);
0255             tmpByteData.append(tmpData, 12);
0256 
0257             int tmpPieceCount = mpGameLogic->getSingleGame(gameID)->getPieceCount();
0258             formByteFromInt(tmpPieceCount, tmpData);
0259             tmpByteData.append(tmpData, 4);
0260 
0261             for (int i = 0; i < tmpPieceCount; ++i) {
0262                 mpGameLogic->getSingleGame(gameID)->getPiece(i)->encodeData((unsigned char *)tmpData + i * 4);
0263             }
0264             tmpByteData.append(tmpData, tmpPieceCount * 4);
0265 
0266             tmpByteCount = mpGameLogic->getSingleGame(gameID)->getField()->encodeData((unsigned char *)tmpData);
0267             tmpByteData.append((char)tmpByteCount);
0268             tmpByteData.append(tmpData, tmpByteCount);
0269 
0270             formByteFromInt(mpGameLogic->getSingleGame(gameID)->getField()->getModifyID(), tmpData);
0271             tmpByteData.append(tmpData, 4);
0272 
0273             QHostAddress tmpIP;
0274             quint16 tmpPort;
0275             parseIPString(*it, &tmpIP, &tmpPort);
0276             mpServerSocket->writeDatagram(tmpByteData, tmpIP, tmpPort);
0277 
0278             //printf("  Sending data to %d @ %s\n", gameID, it->toStdString().c_str());
0279             return;
0280         }
0281     }
0282 }
0283 
0284 void KBlocksNetServer::sendGameOver()
0285 {
0286     QHostAddress tmpIP;
0287     quint16 tmpPort;
0288     QByteArray tmpByteData;
0289 
0290     QList<QString>::iterator it;
0291     for (it = mPlayerIPList.begin(); it != mPlayerIPList.end(); ++it) {
0292         parseIPString(*it, &tmpIP, &tmpPort);
0293 
0294         tmpByteData.append((char)127);
0295         tmpByteData.append((char) - 1);
0296         tmpByteData.append((char)0);
0297 
0298         mpServerSocket->writeDatagram(tmpByteData, tmpIP, tmpPort);
0299     }
0300 }
0301 
0302 void KBlocksNetServer::sendGuiData(const QString &addr)
0303 {
0304     char tmpData[256];
0305     int tmpByteCount;
0306     QByteArray tmpByteData;
0307 
0308     if (!mGameStarted) {
0309         return;
0310     }
0311 
0312     QHostAddress tmpIP;
0313     quint16 tmpPort;
0314     parseIPString(addr, &tmpIP, &tmpPort);
0315 
0316     for (int gameID = 0; gameID < mGameCount; ++gameID) {
0317         tmpByteData.clear();
0318 
0319         tmpByteData.append((char)gameID);
0320 
0321         formByteFromInt(maGameScoreList[gameID]->getScorePoint(), tmpData + 0);
0322         formByteFromInt(maGameScoreList[gameID]->getLineCount(),  tmpData + 4);
0323         formByteFromInt(maGameScoreList[gameID]->getGameLevel(),  tmpData + 8);
0324         tmpByteData.append(tmpData, 12);
0325 
0326         int tmpPieceCount = mpGameLogic->getSingleGame(gameID)->getPieceCount();
0327         formByteFromInt(tmpPieceCount, tmpData);
0328         tmpByteData.append(tmpData, 4);
0329 
0330         for (int i = 0; i < tmpPieceCount; ++i) {
0331             mpGameLogic->getSingleGame(gameID)->getPiece(i)->encodeData((unsigned char *)tmpData + i * 4);
0332         }
0333         tmpByteData.append(tmpData, tmpPieceCount * 4);
0334 
0335         tmpByteCount = mpGameLogic->getSingleGame(gameID)->getField()->encodeData((unsigned char *)tmpData);
0336         tmpByteData.append((char)tmpByteCount);
0337         tmpByteData.append(tmpData, tmpByteCount);
0338 
0339         formByteFromInt(mpGameLogic->getSingleGame(gameID)->getField()->getModifyID(), tmpData);
0340         tmpByteData.append(tmpData, 4);
0341 
0342         mpServerSocket->writeDatagram(tmpByteData, tmpIP, tmpPort);
0343     }
0344 }
0345 
0346 int KBlocksNetServer::parseRemoteData(const QByteArray &data, const QString &addr)
0347 {
0348     int result = -1;
0349     QString input = QString::fromLatin1(data);
0350 
0351     if (input.contains(QStringLiteral("|ap|"))) {
0352         addPlayerIP((int)(data[4] - '0'), data, addr);
0353         //printf("Added player @ %s\n", input.toStdString().c_str());
0354     } else if (input.contains(QStringLiteral("|dp|"))) {
0355         delPlayerIP(addr);
0356         //printf("Deleted player @ %s\n", input.toStdString().c_str());
0357     } else if (input.contains(QStringLiteral("|rp|"))) {
0358         result = parsePlayerReply(data, addr);
0359         //printf("Received Player Data @ %s\n", input.toStdString().c_str());
0360     } else if (input.contains(QStringLiteral("|rg|"))) {
0361         sendGuiData(addr);
0362         //printf("Send Gui Data @ %s\n", input.toStdString().c_str());
0363     } else if (input.contains(QStringLiteral("|s|"))) {
0364         if (!mGameStarted) {
0365             mpGameLogic->startGame(mGameCount);
0366             mGameStarted = true;
0367             for (int i = 0; i < mGameCount; i++) {
0368                 sendPlayerData(i);
0369             }
0370             //printf("Game started\n");
0371         }
0372     } else if (input.contains(QStringLiteral("|c|"))) {
0373         if (mGameStarted) {
0374             mGameStarted = false;
0375             mpGameLogic->stopGame();
0376             //printf("Game stopped\n");
0377         }
0378     } else {
0379         printf("Error transmission data : %s\n", data.data());
0380     }
0381 
0382     return result;
0383 }
0384 
0385 int KBlocksNetServer::parsePlayerReply(const QByteArray &data, const QString &addr)
0386 {
0387     if (!mGameStarted) {
0388         return -1;
0389     }
0390 
0391     if (mPlayerMapping.find(addr) == mPlayerMapping.end()) {
0392         return -1;
0393     }
0394 
0395     int gameID = mPlayerMapping[addr];
0396     KBlocksSingleGame *mpSingleGame = mpGameLogic->getSingleGame(gameID);
0397     if (mpSingleGame == nullptr) {
0398         return -1;
0399     }
0400 
0401     int counter = 4;
0402     while (data[counter] != '|') {
0403         switch (data[counter] - '0') {
0404         case PlayerAction_Move_Left:
0405             mpSingleGame->setCurrentPiece(-1, 0, 0);
0406             break;
0407         case PlayerAction_Move_Right:
0408             mpSingleGame->setCurrentPiece(1, 0, 0);
0409             break;
0410         case PlayerAction_Move_Down:
0411             mpSingleGame->setCurrentPiece(0, 1, 0);
0412             break;
0413         case PlayerAction_Push_Down:
0414             while (mpSingleGame->setCurrentPiece(0, 1, 0)) ;
0415             mpSingleGame->forceUpdateGame();
0416             break;
0417         case PlayerAction_Rotate_CW:
0418             mpSingleGame->setCurrentPiece(0, 0, 1);
0419             break;
0420         case PlayerAction_Rotate_CCW:
0421             mpSingleGame->setCurrentPiece(0, 0, -1);
0422             break;
0423         case PlayerAction_None:
0424         default:
0425             break;
0426         }
0427         ++counter;
0428     }
0429 
0430     return gameID;
0431 }
0432 
0433 bool KBlocksNetServer::parseIPString(const QString &input, QHostAddress *ip, quint16 *port)
0434 {
0435     bool result = false;
0436     ip->setAddress(input.left(input.indexOf(QStringLiteral(":"))));
0437     *port = QStringView(input).mid(input.indexOf(QStringLiteral(":")) + 1).toUInt(&result);
0438     return result;
0439 }
0440 
0441 QString KBlocksNetServer::formIPString(const QHostAddress &inAddr, quint16 inPort)
0442 {
0443     QString result = inAddr.toString() + QStringLiteral(":%1").arg(inPort);
0444     return result;
0445 }
0446 
0447 void KBlocksNetServer::formByteFromInt(int value, char *data)
0448 {
0449     data[0] = value & 0x00FF;
0450     data[1] = (value >>  8) & 0x00FF;
0451     data[2] = (value >> 16) & 0x00FF;
0452     data[3] = (value >> 24) & 0x00FF;
0453 }
0454 
0455 void KBlocksNetServer::printGameResult()
0456 {
0457     printf("-------- Game Report --------\n");
0458     for (int gameID = 0; gameID < mGameCount; gameID++) {
0459         QString tmpPlayerName = mPlayerName[gameID];
0460         printf("Game ID : %d\n", gameID);
0461         printf("\tPlayer Name : %s\n", tmpPlayerName.toLatin1().constData());
0462         printf("\tTotal Score : %d\n", maGameScoreList[gameID]->getLineCount());
0463     }
0464     printf("-----------------------------\n");
0465 }
0466 
0467 #include "moc_KBlocksNetServer.cpp"