File indexing completed on 2024-04-21 04:02:09

0001 /******************************************************************************
0002 *   KBlocks, a falling blocks game by KDE                                     *
0003 *   SPDX-FileCopyrightText: 2009-2021 Mauricio Piacentini <mauricio@tabuleiro.com>      *
0004 *                           Zhongjie Cai <squall.leonhart.cai@gmail.com>      *
0005 *                           Julian Helfferich <julian.helfferich@mailbox.org> *
0006 *                                                                             *
0007 *   SPDX-License-Identifier: GPL-2.0-or-later
0008 ******************************************************************************/
0009 #include <ctime>
0010 #include <string>
0011 #include <vector>
0012 
0013 #include <KAboutData>
0014 #include <KCrash>
0015 #include <KDBusService>
0016 #include <KLocalizedString>
0017 
0018 #include <KGameThemeProvider>
0019 
0020 #include <QString>
0021 #include <QByteArray>
0022 #include <QApplication>
0023 #include <QCommandLineParser>
0024 #include <QCommandLineOption>
0025 
0026 using namespace std;
0027 
0028 #include "KBlocksConfigManager.h"
0029 
0030 #include "KBlocksGameLogic.h"
0031 #include "KBlocksPlayManager.h"
0032 #include "KBlocksPlayNetwork.h"
0033 #include "KBlocksWin.h"
0034 #include "KBlocksDisplay.h"
0035 #include "KBlocksRepWin.h"
0036 
0037 #include "KBlocksAppThread.h"
0038 
0039 #include "AI/KBlocksAIPlayer.h"
0040 #include "KBlocksKeyboardPlayer.h"
0041 
0042 #include "KBlocksNetServer.h"
0043 #include "KBlocksNetClient.h"
0044 
0045 #include "kblocks_version.h"
0046 #include "settings.h"
0047 
0048 KBlocksGameLogic *mpKBlocksGameLogic;
0049 KBlocksPlayManager *mpKBlocksPlayManager;
0050 KBlocksPlayNetwork *mpKBlocksPlayNetwork;
0051 KBlocksWin *mpKBlocksWindow;
0052 KBlocksDisplay *mpKBlocksDisplay;
0053 KBlocksAppThread *mpKBlocksAppThread;
0054 
0055 KBlocksAIPlayer **maAIPlayers;
0056 KBlocksKeyboardPlayer **maHumanPlayers;
0057 
0058 enum KBlocksGameMode {
0059     KBlocksGame_DesktopMode = 0,
0060     KBlocksGame_EngineMode,
0061     KBlocksGame_GuiMode,
0062     KBlocksGame_PlayerMode,
0063     KBlocksGame_ReplayMode,
0064     KBlocksGame_MaxMode_Count
0065 };
0066 
0067 static
0068 void initThemeProvider(KGameThemeProvider &themeProvider)
0069 {
0070     themeProvider.discoverThemes(
0071         QStringLiteral("themes"),   // theme data location
0072         QStringLiteral("default")); // default theme name
0073 
0074     const QByteArray themeIdentifier = Settings::theme().toUtf8();
0075     const QList<const KGameTheme *> themes = themeProvider.themes();
0076     for (auto* theme : themes) {
0077         if (theme->identifier() == themeIdentifier) {
0078             themeProvider.setCurrentTheme(theme);
0079             break;
0080         }
0081     }
0082 }
0083 
0084 int gameDesktopMode(const QApplication &app)
0085 {
0086     // Desktop User Mode
0087     mpKBlocksGameLogic = new KBlocksGameLogic(2);
0088     mpKBlocksGameLogic->setGameSeed(std::time(nullptr));
0089     mpKBlocksGameLogic->setGamePunish(true);
0090     mpKBlocksGameLogic->setGameStandbyMode(true);
0091     mpKBlocksGameLogic->setInitInterval(500);
0092     mpKBlocksGameLogic->setLevelUpInterval(25);
0093 
0094     mpKBlocksPlayManager = new KBlocksPlayManager(mpKBlocksGameLogic, 2);
0095 
0096     KGameThemeProvider themeProvider{QByteArray()}; // empty config key to disable internal config
0097     initThemeProvider(themeProvider);
0098 
0099     KBlocksGraphics graphics(themeProvider.currentTheme());
0100     KBlocksSound sound(themeProvider.currentTheme());
0101 
0102     mpKBlocksWindow = new KBlocksWin(
0103         mpKBlocksGameLogic,
0104         &graphics,
0105         &sound,
0106         &themeProvider,
0107         mpKBlocksPlayManager,
0108         2,
0109         1
0110     );
0111 
0112     mpKBlocksWindow->setupGUILayout();
0113     mpKBlocksWindow->setUpdateInterval(50);
0114     mpKBlocksWindow->setGamesPerLine(4);
0115     mpKBlocksWindow->setGameAnimEnabled(true);
0116     mpKBlocksWindow->setWaitForAllUpdate(true);
0117     mpKBlocksWindow->show();
0118 
0119     return app.exec();
0120 }
0121 
0122 int gameEngineMode(KBlocksConfigManager *config)
0123 {
0124     int gameCount;
0125     bool sameSeq;
0126     bool hasAttack;
0127     bool standbyMode;
0128     bool hasHuman;
0129     bool sendLimit;
0130     string serverIP;
0131 
0132     string recordFile;
0133     string recordType;
0134     bool recordBinary;
0135 
0136     config->GetKeyInt("Engine", "GameCount", &gameCount, 1);
0137     config->GetKeyBool("Engine", "SameSequence", &sameSeq, true);
0138     config->GetKeyBool("Engine", "HasAttack", &hasAttack, true);
0139     config->GetKeyBool("Engine", "Synchronized", &standbyMode, false);
0140     config->GetKeyBool("Engine", "HasHuman", &hasHuman, false);
0141     config->GetKeyBool("Engine", "SendLimit", &sendLimit, false);
0142     config->GetKeyString("Engine", "ServerIP", &serverIP, "127.0.0.1:10086");
0143 
0144     config->GetKeyString("RecordReplay", "Record", &recordFile, "");
0145     config->GetKeyString("RecordReplay", "Type", &recordType, "binary");
0146     recordBinary = recordType.find("text") != 0;
0147 
0148     printf("Creating game engine...\n");
0149     printf("\tGame Count    = %d\n", gameCount);
0150     printf("\tSame Sequence = %s\n", sameSeq ? "true" : "false");
0151     printf("\tHas Attack    = %s\n", hasAttack ? "true" : "false");
0152     printf("\tSynchronized  = %s\n", standbyMode ? "true" : "false");
0153     printf("\tHas Human     = %s\n", hasHuman ? "true" : "false");
0154     printf("\tSpeed Limit   = %s\n", sendLimit ? "true" : "false");
0155     mpKBlocksGameLogic = new KBlocksGameLogic(gameCount, true);
0156     mpKBlocksGameLogic->setGameSeed(sameSeq ? std::time(nullptr) : -std::time(nullptr));
0157     mpKBlocksGameLogic->setGamePunish(hasAttack);
0158     mpKBlocksGameLogic->setGameStandbyMode(standbyMode);
0159     mpKBlocksGameLogic->setInitInterval(hasHuman ? 500 : 0);
0160     mpKBlocksGameLogic->setLevelUpInterval(hasHuman ? 25 : 0);
0161     printf("Done...\n");
0162 
0163     printf("Creating network server...\n");
0164     printf("\tServer IP = %s\n", serverIP.c_str());
0165     printf("\tRecord File = %s\n", recordFile.c_str());
0166     printf("\tRecord Type = %s\n", recordBinary ? "Binary" : "Text");
0167     KBlocksNetServer *mpKBlocksServer = new KBlocksNetServer(mpKBlocksGameLogic, QString::fromStdString(serverIP));
0168     mpKBlocksServer->setSendLength(sendLimit ? 10 : 0, sendLimit ? 1 : 0);
0169     mpKBlocksServer->setRecordFile(recordFile.c_str(), recordBinary);
0170     printf("Done...\n");
0171 
0172     printf("Executing game engine and network server...\n");
0173     return mpKBlocksServer->executeGame(gameCount, standbyMode);
0174 }
0175 
0176 int gameGuiMode(KBlocksConfigManager *config, const QApplication &app)
0177 {
0178     int gameCount;
0179     int gamesPerLine;
0180     int updateInterval;
0181     int localPort;
0182     string serverIP;
0183 
0184     config->GetKeyInt("Gui", "GameCount", &gameCount, 1);
0185     config->GetKeyInt("Gui", "GamesPerLine", &gamesPerLine, 4);
0186     config->GetKeyInt("Gui", "UpdateInterval", &updateInterval, 1000);
0187     config->GetKeyInt("Gui", "LocalPort", &localPort, 10088);
0188     config->GetKeyString("Gui", "ServerIP", &serverIP, "127.0.0.1:10086");
0189 
0190     printf("Creating game gui...\n");
0191     printf("\tGame Count      = %d\n", gameCount);
0192     printf("\tGames Per Line  = %d\n", gamesPerLine);
0193     printf("\tUpdate Interval = %d\n", updateInterval);
0194     printf("\tLocal Port      = %d\n", localPort);
0195     printf("\tServer IP       = %s\n", serverIP.c_str());
0196 
0197     KGameThemeProvider themeProvider{QByteArray()}; // empty config key to disable internal config
0198     initThemeProvider(themeProvider);
0199 
0200     KBlocksGraphics graphics(themeProvider.currentTheme());
0201     KBlocksSound sound(themeProvider.currentTheme());
0202 
0203     mpKBlocksDisplay = new KBlocksDisplay(
0204         &graphics,
0205         &sound,
0206         gameCount,
0207         serverIP,
0208         localPort
0209     );
0210     mpKBlocksDisplay->setGamesPerLine(gamesPerLine);
0211     mpKBlocksDisplay->setUpdateInterval(updateInterval);
0212     mpKBlocksDisplay->show();
0213     printf("Done...\n");
0214 
0215     printf("Starting game gui...\n");
0216     mpKBlocksDisplay->startDisplay();
0217     printf("Done...\n");
0218 
0219     return app.exec();
0220 }
0221 
0222 int gameReplayMode(KBlocksConfigManager *config, const QApplication &app)
0223 {
0224     int gamesPerLine;
0225     int updateInterval;
0226     int stepLength;
0227     string snapshotFolder;
0228     string snapshotFile;
0229     string recordFile;
0230     string recordType;
0231     bool recordBinary;
0232 
0233     config->GetKeyInt("RecordReplay", "GamesPerLine", &gamesPerLine, 4);
0234     config->GetKeyInt("RecordReplay", "UpdateInterval", &updateInterval, 100);
0235     config->GetKeyInt("RecordReplay", "StepLength", &stepLength, 100);
0236     config->GetKeyString("RecordReplay", "SnapshotFolder", &snapshotFolder, "./snapshot/");
0237     config->GetKeyString("RecordReplay", "SnapshotFile", &snapshotFile, "");
0238     config->GetKeyString("RecordReplay", "Record", &recordFile, "");
0239     config->GetKeyString("RecordReplay", "Type", &recordType, "binary");
0240     recordBinary = recordType.find("text") != 0;
0241 
0242     if (recordFile.empty()) {
0243         printf("Error loading replay file: File name is empty!\n");
0244         return -1;
0245     }
0246 
0247     printf("Creating game gui...\n");
0248     printf("\tGames Per Line  = %d\n", gamesPerLine);
0249     printf("\tUpdate Interval = %d\n", updateInterval);
0250     printf("\tStep Length     = %d\n", stepLength);
0251     printf("\tSnapshot Folder = %s\n", snapshotFolder.c_str());
0252     printf("\tSnapshot File   = %s\n", snapshotFile.c_str());
0253     printf("\tRecord File     = %s\n", recordFile.c_str());
0254     printf("\tRecord Type     = %s\n", recordBinary ? "Binary" : "Text");
0255 
0256     KGameThemeProvider themeProvider{QByteArray()}; // empty config key to disable internal config
0257     initThemeProvider(themeProvider);
0258 
0259     KBlocksGraphics graphics(themeProvider.currentTheme());
0260     KBlocksSound sound(themeProvider.currentTheme());
0261 
0262     KBlocksRepWin *mpKBlocksRepWin = new KBlocksRepWin(
0263         &graphics,
0264         &sound,
0265         recordFile.c_str(),
0266         recordBinary
0267     );
0268     if (!mpKBlocksRepWin->replayLoaded()) {
0269         printf("Error loading replay file: Failed to load replay file!\n");
0270         return -2;
0271     }
0272     mpKBlocksRepWin->setGamesPerLine(gamesPerLine);
0273     mpKBlocksRepWin->setUpdateInterval(updateInterval);
0274     mpKBlocksRepWin->setReplayStepLength(stepLength);
0275     mpKBlocksRepWin->setSnapshotFolder(QString::fromStdString(snapshotFolder));
0276     mpKBlocksRepWin->setSnapshotFilename(QString::fromStdString(snapshotFile));
0277     mpKBlocksRepWin->show();
0278     printf("Done...\n");
0279 
0280     printf("Starting game gui...\n");
0281     mpKBlocksRepWin->startReplay();
0282     printf("Done...\n");
0283 
0284     return app.exec();
0285 }
0286 
0287 int gamePlayerMode(KBlocksConfigManager *config, const QApplication &app)
0288 {
0289     bool hasHuman = false;
0290     int playerCount;
0291     int localPort;
0292     string serverIP;
0293 
0294     config->GetKeyInt("Player", "PlayerCount", &playerCount, 1);
0295     config->GetKeyInt("Player", "LocalPort", &localPort, 10090);
0296     config->GetKeyString("Player", "ServerIP", &serverIP, "127.0.0.1:10086");
0297 
0298     printf("Creating game player manager...\n");
0299     printf("\tPlayer Count = %d\n", playerCount);
0300     printf("\tLocal Port   = %d\n", localPort);
0301     printf("\tServer IP    = %s\n", serverIP.c_str());
0302     mpKBlocksPlayNetwork = new KBlocksPlayNetwork(playerCount, serverIP, localPort);
0303     printf("Done...\n");
0304 
0305     printf("Adding game players...\n");
0306     maAIPlayers = new KBlocksAIPlayer*[playerCount];
0307     maHumanPlayers = new KBlocksKeyboardPlayer*[playerCount];
0308     for (int i = 0; i < playerCount; i++) {
0309         maAIPlayers[i] = nullptr;
0310         maHumanPlayers[i] = nullptr;
0311 
0312         char tmpBuf[256];
0313         sprintf(tmpBuf, "PlayerType%d", i + 1);
0314         string tmpType = string(tmpBuf);
0315         sprintf(tmpBuf, "PlayerName%d", i + 1);
0316         string tmpName = string(tmpBuf);
0317         config->GetKeyString("Player", tmpType, &tmpType, "AI");
0318         config->GetKeyString("Player", tmpName, &tmpName, "NoName");
0319 
0320         printf("\tNew Player (%d) Type = %s\n", i, tmpType.c_str());
0321         printf("\t               Name = %s\n", tmpName.c_str());
0322         if (tmpType.find("ai") != tmpType.npos) {
0323             maAIPlayers[i] = new KBlocksAIPlayer(tmpName);
0324             mpKBlocksPlayNetwork->addGamePlayer(maAIPlayers[i]);
0325         } else if (tmpType.find("human") != tmpType.npos) {
0326             maHumanPlayers[i] = new KBlocksKeyboardPlayer(NULL, tmpName, true);
0327             mpKBlocksPlayNetwork->addGamePlayer(maHumanPlayers[i]);
0328             hasHuman = true;
0329         }
0330     }
0331     printf("Done...\n");
0332 
0333     printf("Starting play manager...\n");
0334     mpKBlocksPlayNetwork->startGame();
0335 
0336     int ret = 0;
0337     if (hasHuman) {
0338         mpKBlocksAppThread = new KBlocksAppThread(mpKBlocksPlayNetwork);
0339         printf("Executing play manager...\n");
0340         mpKBlocksAppThread->start();
0341 
0342         printf("Executing keyboard window...\n");
0343         ret = app.exec();
0344 
0345         printf("Terminating play manager execution...\n");
0346         mpKBlocksPlayNetwork->cancelExecute();
0347     } else {
0348         printf("Executing...\n");
0349         ret = mpKBlocksPlayNetwork->execute();
0350     }
0351 
0352     printf("Stopping play manager...\n");
0353     mpKBlocksPlayNetwork->stopGame();
0354 
0355     printf("Clearing game players...\n");
0356     mpKBlocksPlayNetwork->clearGamePlayer();
0357 
0358     printf("Exit program...\n");
0359     return ret;
0360 }
0361 
0362 int main(int argc, char *argv[])
0363 {
0364     QApplication app(argc, argv);
0365 
0366 #ifdef Q_OS_WINDOWS
0367     QApplication::setStyle(QStringLiteral("Breeze"));
0368 #endif
0369 
0370     KLocalizedString::setApplicationDomain(QByteArrayLiteral("kblocks"));
0371 
0372     // Game abouts...
0373 
0374     KAboutData aboutData(QStringLiteral("kblocks"),
0375                          i18n("KBlocks"),
0376                          QStringLiteral(KBLOCKS_VERSION_STRING),
0377                          i18n("A falling blocks game by KDE"),
0378                          KAboutLicense::GPL,
0379                          i18n("(c) 2007, Mauricio Piacentini"),
0380                          QString(),
0381                          QStringLiteral("https://apps.kde.org/kblocks"));
0382     aboutData.addAuthor(i18n("Mauricio Piacentini"), i18n("Author"), QStringLiteral("piacentini@kde.org"));
0383     aboutData.addAuthor(i18n("Dirk Leifeld"), i18n("Developer"), QStringLiteral("dirkleifeld@yahoo.de"));
0384     aboutData.addAuthor(i18n("Zhongjie Cai"), i18n("New design of KBlocks for AI and tetris research platform"), QStringLiteral("squall.leonhart.cai@gmail.com"));
0385     aboutData.addCredit(i18n("Johann Ollivier Lapeyre"), i18n("Oxygen art for KDE4"), QStringLiteral("johann.ollivierlapeyre@gmail.com"));
0386 
0387     KAboutData::setApplicationData(aboutData);
0388     QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("kblocks")));
0389 
0390     KCrash::initialize();
0391 
0392     // Command line argument options
0393     QCommandLineParser parser;
0394     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("mode"), i18n("Setup kblocks game running mode.\n\t0 = Desktop Mode\t1 = Game Engine Mode\n\t2 = Gui Mode\t3 = Player Mode"), QStringLiteral("game mode"), QStringLiteral("0")));
0395     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("conf"), i18n("Setup the configuration file for tetris researcher mode. Not for desktop users."), QStringLiteral("configuration file"), QStringLiteral("default.conf")));
0396     aboutData.setupCommandLine(&parser);
0397     parser.process(app);
0398     aboutData.processCommandLine(&parser);
0399 
0400     KDBusService service;
0401 
0402     // Get game mode
0403     int mGameMode = parser.value(QStringLiteral("mode")).toInt();
0404 
0405     QByteArray tmpFileArray = parser.value(QStringLiteral("conf")).toLatin1();
0406     const char *tmpFileChar = tmpFileArray.data();
0407     KBlocksConfigManager config;
0408     config.LoadConfigFile(string(tmpFileChar));
0409 
0410     int mResult = 0;
0411     switch (mGameMode) {
0412     case KBlocksGame_DesktopMode:
0413         mResult = gameDesktopMode(app);
0414         break;
0415     case KBlocksGame_EngineMode:
0416         mResult = gameEngineMode(&config);
0417         break;
0418     case KBlocksGame_GuiMode:
0419         mResult = gameGuiMode(&config, app);
0420         break;
0421     case KBlocksGame_PlayerMode:
0422         mResult = gamePlayerMode(&config, app);
0423         break;
0424     case KBlocksGame_ReplayMode:
0425         mResult = gameReplayMode(&config, app);
0426         break;
0427     }
0428 
0429     return mResult;
0430 }
0431