File indexing completed on 2024-05-05 05:40:24

0001 /***************************************************************************
0002  *  Copyright (C) 2019 by Renaud Guezennec                               *
0003  *   http://www.rolisteam.org/contact                                      *
0004  *                                                                         *
0005  *   This software is free software; you can redistribute it and/or modify *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0019  ***************************************************************************/
0020 #include "controller/gamecontroller.h"
0021 
0022 #include <QTimer>
0023 
0024 #include "common/logcontroller.h"
0025 #include "common/remotelogcontroller.h"
0026 
0027 #include "data/campaigneditor.h"
0028 
0029 #include "data/media.h"
0030 #include "model/profilemodel.h"
0031 #include "preferences/preferencesmanager.h"
0032 #include "services/tipchecker.h"
0033 #include "services/updatechecker.h"
0034 #include "worker/autosavecontroller.h"
0035 #include "worker/iohelper.h"
0036 #include "worker/messagehelper.h"
0037 #include "worker/modelhelper.h"
0038 
0039 #include "network/connectionprofile.h"
0040 
0041 GameController::GameController(const QString& appname, const QString& version, QClipboard* clipboard, QObject* parent)
0042     : QObject(parent)
0043     , m_diceParser(new DiceRoller)
0044     , m_logController(new LogController(true))
0045     , m_remoteLogCtrl(new RemoteLogController())
0046     , m_networkCtrl(new NetworkController)
0047     , m_playerController(new PlayerController(m_networkCtrl.get()))
0048     , m_preferencesDialogController(new PreferencesController)
0049     , m_campaignManager(new campaign::CampaignManager(m_diceParser.get()))
0050     , m_contentCtrl(new ContentController(m_campaignManager.get(), m_playerController->model(),
0051                                           m_playerController->characterModel(), clipboard, m_networkCtrl.get()))
0052     , m_preferences(new PreferencesManager(appname, QString("%1_%2/preferences").arg(appname, version)))
0053     , m_instantMessagingCtrl(new InstantMessagingController(m_diceParser.get(), m_playerController->model()))
0054     , m_audioCtrl(new AudioController(m_campaignManager.get(), m_preferences.get()))
0055     , m_version(version)
0056     , m_undoStack(new QUndoStack)
0057     , m_autoSaveCtrl(new AutoSaveController(m_preferences.get()))
0058 {
0059     m_preferences->readSettings();
0060     postSettingInit();
0061 
0062     m_networkCtrl->setGameController(this);
0063     m_playerController->setGameController(this);
0064     m_contentCtrl->setGameController(this);
0065     m_preferencesDialogController->setGameController(this);
0066 
0067     connect(m_logController.get(), &LogController::sendOffMessage, m_remoteLogCtrl.get(), &RemoteLogController::addLog);
0068     connect(m_networkCtrl.get(), &NetworkController::connectedChanged, this,
0069             [this](bool b)
0070             {
0071                 if(b)
0072                 {
0073                     m_campaignManager->shareModels();
0074                 }
0075                 emit connectedChanged(b);
0076             });
0077 
0078     connect(m_campaignManager.get(), &campaign::CampaignManager::campaignChanged, this,
0079             [this]()
0080             {
0081                 auto cm= m_campaignManager->campaign();
0082                 m_contentCtrl->setMediaRoot(cm->directory(campaign::Campaign::Place::MEDIA_ROOT));
0083                 m_logController->setCurrentPath(QString("%1/rolisteam.log").arg(cm->rootDirectory()));
0084             });
0085     connect(m_campaignManager->campaign(), &campaign::Campaign::rootDirectoryChanged, this,
0086             [this]()
0087             {
0088                 auto cm= m_campaignManager->campaign();
0089                 m_contentCtrl->setMediaRoot(cm->directory(campaign::Campaign::Place::MEDIA_ROOT));
0090                 emit campaignRootChanged();
0091             });
0092     connect(m_campaignManager->editor(), &campaign::CampaignEditor::performCommand, this, &GameController::addCommand);
0093 
0094     // clang-format off
0095     connect(m_autoSaveCtrl.get(), &AutoSaveController::saveData, m_campaignManager.get(), &campaign::CampaignManager::saveCampaign);
0096     connect(m_playerController.get(), &PlayerController::gameMasterIdChanged, m_contentCtrl.get(), &ContentController::setGameMasterId);
0097     connect(m_playerController.get(), &PlayerController::performCommand, this, &GameController::addCommand);
0098     connect(m_playerController.get(), &PlayerController::localPlayerIdChanged, m_remoteLogCtrl.get(), &RemoteLogController::setLocalUuid);
0099     connect(m_playerController.get(), &PlayerController::localPlayerIdChanged, m_remoteLogCtrl.get(), &RemoteLogController::setLocalUuid);
0100     connect(m_playerController.get(), &PlayerController::localPlayerIdChanged, m_instantMessagingCtrl.get(), &InstantMessagingController::setLocalId);
0101     connect(m_playerController.get(), &PlayerController::localPlayerIdChanged, m_contentCtrl.get(), &ContentController::setLocalId);
0102     connect(m_playerController.get(), &PlayerController::localPlayerChanged, m_contentCtrl.get(), [this](){
0103         auto local = m_playerController->localPlayer();
0104         if(!local)
0105             return;
0106         m_contentCtrl->setLocalColor(local->getColor());
0107     });
0108 
0109     connect(m_contentCtrl.get(), &ContentController::performCommand, this, &GameController::addCommand);
0110     auto popCommand = [this]()
0111     {
0112         m_undoStack->setIndex(m_undoStack->index()-1);
0113     };
0114     connect(m_contentCtrl.get(), &ContentController::popCommand, this, popCommand);
0115 
0116     connect(m_networkCtrl.get(), &NetworkController::isGMChanged, m_campaignManager.get(), &campaign::CampaignManager::setLocalIsGM);
0117     connect(m_networkCtrl.get(), &NetworkController::isGMChanged, m_audioCtrl.get(), &AudioController::setLocalIsGM);
0118     connect(m_campaignManager.get(), &campaign::CampaignManager::campaignLoaded, this, &GameController::dataLoaded);
0119     connect(m_campaignManager.get(), &campaign::CampaignManager::campaignLoaded, this, [this](){
0120         m_campaignManager->diceparser();
0121     });
0122     // clang-format on
0123 
0124     m_contentCtrl->setGameMasterId(m_playerController->gameMasterId());
0125     m_remoteLogCtrl->setLocalUuid(m_playerController->localPlayerId());
0126     m_contentCtrl->setLocalId(m_playerController->localPlayerId());
0127     m_instantMessagingCtrl->setLocalId(m_playerController->localPlayerId());
0128     m_audioCtrl->setLocalIsGM(m_networkCtrl->isGM());
0129     m_campaignManager->setLocalIsGM(m_networkCtrl->isGM());
0130 
0131     m_remoteLogCtrl->setAppId(0);
0132 }
0133 GameController::~GameController()= default;
0134 
0135 void GameController::clear()
0136 {
0137     m_playerController->clear();
0138     m_undoStack->clear();
0139 }
0140 
0141 void GameController::postSettingInit()
0142 {
0143     // Log controller
0144     auto logDebug= m_preferences->value(QStringLiteral("LogDebug"), false).toBool();
0145     // m_logController->setMessageHandler(logDebug);
0146 
0147     auto LogResearch= m_preferences->value(QStringLiteral("LogResearch"), false).toBool();
0148     auto dataCollection= m_preferences->value(QStringLiteral("dataCollection"), false).toBool();
0149     m_logController->setSignalInspection(logDebug && (LogResearch || dataCollection));
0150 
0151     LogController::StorageModes mode= LogController::Gui | LogController::Console;
0152 
0153     if(localIsGM())
0154         mode|= LogController::File;
0155 
0156     if((LogResearch && dataCollection))
0157     {
0158         /*      m_logController->listenObjects(this);
0159               mode= LogController::Gui | LogController::Network;
0160               setFocusPolicy(Qt::StrongFocus);
0161               auto clipboard= QGuiApplication::clipboard();
0162               connect(clipboard, &QClipboard::dataChanged, this, [clipboard, this]() {
0163                   auto text= clipboard->text();
0164                   auto mime= clipboard->mimeData();
0165                   text.append(QStringLiteral("\n%1").arg(mime->formats().join("|")));
0166                   m_logController->manageMessage(QStringLiteral("Clipboard data changed: %1").arg(text),
0167                       LogController::Search);
0168               });*/
0169     }
0170     m_logController->setCurrentModes(mode);
0171 }
0172 
0173 PreferencesManager* GameController::preferencesManager() const
0174 {
0175     return m_preferences.get();
0176 }
0177 
0178 void GameController::setCampaignRoot(const QString& path)
0179 {
0180     auto campaign= m_campaignManager->campaign();
0181     campaign->setRootDirectory(path);
0182 }
0183 
0184 void GameController::setVersion(const QString& version)
0185 {
0186     if(version == m_version)
0187         return;
0188     m_version= version;
0189     emit versionChanged();
0190 }
0191 
0192 void GameController::setLocalPlayerId(const QString& id)
0193 {
0194     m_remoteLogCtrl->setLocalUuid(id);
0195 }
0196 
0197 void GameController::newMedia(const std::map<QString, QVariant>& map)
0198 {
0199     m_contentCtrl->newMedia(m_campaignManager->editor(), map);
0200 }
0201 
0202 void GameController::openMedia(const std::map<QString, QVariant>& map)
0203 {
0204     QMap<QString, QVariant> other(map);
0205     if(localIsGM())
0206     {
0207         if(other.contains(Core::keys::KEY_URL) && !other.contains(Core::keys::KEY_INTERNAL))
0208         {
0209             auto path= other.value(Core::keys::KEY_URL).toUrl().toLocalFile();
0210             other[Core::keys::KEY_PATH]= m_campaignManager->importFile(QUrl::fromLocalFile(path));
0211         }
0212         else if(other.contains(Core::keys::KEY_DATA))
0213         {
0214             auto name= other.value(Core::keys::KEY_NAME).toString();
0215             auto data= other.value(Core::keys::KEY_DATA).toByteArray();
0216             other[Core::keys::KEY_PATH]= m_campaignManager->createFileFromData(name, data);
0217         }
0218     }
0219     m_contentCtrl->openMedia(other.toStdMap());
0220 }
0221 
0222 void GameController::save()
0223 {
0224     if(localIsGM())
0225     {
0226         ModelHelper::saveSession(m_contentCtrl.get());
0227         m_campaignManager->saveCampaign();
0228     }
0229     ModelHelper::saveAudioController(m_audioCtrl.get());
0230 }
0231 
0232 void GameController::saveAs(const QString& path)
0233 {
0234     ModelHelper::saveSession(m_contentCtrl.get());
0235     ModelHelper::saveAudioController(m_audioCtrl.get());
0236     m_campaignManager->copyCampaign(path);
0237 }
0238 
0239 void GameController::setUpdateAvailable(bool available)
0240 {
0241     if(available == m_updateAvailable)
0242         return;
0243     m_updateAvailable= available;
0244     emit updateAvailableChanged();
0245 }
0246 
0247 ContentController* GameController::contentController() const
0248 {
0249     return m_contentCtrl.get();
0250 }
0251 
0252 LogController* GameController::logController() const
0253 {
0254     return m_logController.get();
0255 }
0256 
0257 void GameController::addErrorLog(const QString& message, const QString& cat)
0258 {
0259     m_logController->manageMessage(message, cat, LogController::Error);
0260 }
0261 
0262 void GameController::addWarningLog(const QString& message, const QString& cat)
0263 {
0264     m_logController->manageMessage(message, cat, LogController::Warning);
0265 }
0266 
0267 void GameController::addFeatureLog(const QString& message, const QString& cat)
0268 {
0269     m_logController->manageMessage(message, cat, LogController::Features);
0270 }
0271 
0272 void GameController::addInfoLog(const QString& message, const QString& cat)
0273 {
0274     m_logController->manageMessage(message, cat, LogController::Info);
0275 }
0276 
0277 void GameController::addSearchLog(const QString& message, const QString& cat)
0278 {
0279     m_logController->manageMessage(message, cat, LogController::Search);
0280 }
0281 
0282 NetworkController* GameController::networkController() const
0283 {
0284     return m_networkCtrl.get();
0285 }
0286 
0287 PlayerController* GameController::playerController() const
0288 {
0289     return m_playerController.get();
0290 }
0291 
0292 QString GameController::remoteVersion() const
0293 {
0294     return m_remoteVersion;
0295 }
0296 
0297 void GameController::startCheckForUpdates()
0298 {
0299     if(!m_preferences->value(QStringLiteral("MainWindow::MustBeChecked"), true).toBool())
0300         return;
0301 
0302     auto updateChecker= new UpdateChecker(m_version, this);
0303     updateChecker->startChecking();
0304     connect(updateChecker, &UpdateChecker::checkFinished, this,
0305             [this, updateChecker]()
0306             {
0307                 setUpdateAvailable(updateChecker->needUpdate());
0308                 m_remoteVersion= updateChecker->getLatestVersion();
0309                 updateChecker->deleteLater();
0310             });
0311 }
0312 
0313 void GameController::startIpRetriever()
0314 {
0315     if(m_preferences->value(QStringLiteral("MainWindow::neverDisplayTips"), false).toBool())
0316         return;
0317     TipChecker* tipChecker= new TipChecker(this);
0318     tipChecker->startChecking();
0319 
0320     connect(tipChecker, &TipChecker::checkFinished, this,
0321             [=]()
0322             {
0323                 auto id= m_preferences->value(QStringLiteral("MainWindow::lastTips"), 0).toInt();
0324                 if(tipChecker->hasArticle() && tipChecker->getId() + 1 > id)
0325                 {
0326                     m_tipOfTheDay= {tipChecker->getArticleTitle(), tipChecker->getArticleContent(),
0327                                     tipChecker->getUrl(), tipChecker->getId()};
0328 
0329                     m_preferences->registerValue(QStringLiteral("MainWindow::lastTips"), m_tipOfTheDay.id);
0330                     emit tipOfDayChanged();
0331                 }
0332                 tipChecker->deleteLater();
0333             });
0334 }
0335 
0336 TipOfDay GameController::tipOfDay() const
0337 {
0338     return m_tipOfTheDay;
0339 }
0340 
0341 void GameController::startTipOfDay() {}
0342 
0343 QString GameController::version() const
0344 {
0345     return m_version;
0346 }
0347 
0348 QString GameController::campaignRoot() const
0349 {
0350     return m_campaignManager->campaignDir();
0351 }
0352 
0353 InstantMessagingController* GameController::instantMessagingController() const
0354 {
0355     return m_instantMessagingCtrl.get();
0356 }
0357 
0358 campaign::CampaignManager* GameController::campaignManager() const
0359 {
0360     return m_campaignManager.get();
0361 }
0362 
0363 campaign::Campaign* GameController::campaign() const
0364 {
0365     return m_campaignManager->campaign();
0366 }
0367 
0368 AudioController* GameController::audioController() const
0369 {
0370     return m_audioCtrl.get();
0371 }
0372 
0373 QString GameController::localPlayerId() const
0374 {
0375     return m_playerController->localPlayerId();
0376 }
0377 
0378 bool GameController::localIsGM() const
0379 {
0380     return m_networkCtrl->isGM();
0381 }
0382 
0383 bool GameController::updateAvailable() const
0384 {
0385     return m_updateAvailable;
0386 }
0387 
0388 bool GameController::connected() const
0389 {
0390     return m_networkCtrl->connected();
0391 }
0392 
0393 QUndoStack* GameController::undoStack() const
0394 {
0395     return m_undoStack.get();
0396 }
0397 
0398 DiceRoller* GameController::diceParser() const
0399 {
0400     return m_diceParser.get();
0401 }
0402 
0403 void GameController::setDataFromProfile(int profileIndex)
0404 {
0405     auto profile= m_networkCtrl->profileModel()->getProfile(profileIndex);
0406     m_networkCtrl->setSelectedProfileIndex(profileIndex);
0407 
0408     if(profile == nullptr)
0409         return;
0410 
0411     auto local= m_playerController->localPlayer();
0412 
0413     local->setUuid(profile->playerId());
0414     local->setName(profile->playerName());
0415     local->setColor(profile->playerColor());
0416     local->setAvatar(profile->playerAvatar());
0417     local->setGM(profile->isGM());
0418     if(!local->isGM())
0419     {
0420         auto characters= profile->characters();
0421         std::for_each(
0422             characters.begin(), characters.end(),
0423             [local](const connection::CharacterData& data)
0424             { local->addCharacter(data.m_uuid, data.m_name, data.m_color, data.m_avatarData, data.m_params, false); });
0425     }
0426     /*m_networkCtrl->setHost(profile->address());
0427     m_networkCtrl->setPort(profile->port());
0428     m_networkCtrl->setServerPassword(profile->password());
0429     m_networkCtrl->setIsGM(profile->isGM());
0430     m_networkCtrl->setHosting(profile->isServer());
0431     m_networkCtrl->setAskForGM(profile->isGM());*/
0432 
0433     m_playerController->addPlayer(local);
0434     if(profile->isGM())
0435     {
0436         m_campaignManager->openCampaign(QUrl::fromLocalFile(profile->campaignPath()));
0437     }
0438     else
0439     {
0440         emit dataLoaded({}, {});
0441     }
0442 }
0443 
0444 void GameController::startConnection()
0445 {
0446     m_networkCtrl->startConnection();
0447 }
0448 
0449 void GameController::stopConnection()
0450 {
0451     networkController()->stopConnecting();
0452 }
0453 
0454 void GameController::postConnection()
0455 {
0456     // save data
0457     m_networkCtrl->saveData();
0458 }
0459 
0460 void GameController::aboutToClose()
0461 {
0462     save();
0463     // save data
0464     m_networkCtrl->saveData();
0465 
0466     // close connection
0467     MessageHelper::sendOffGoodBye();
0468     m_networkCtrl->closeServer();
0469     m_preferences->writeSettings();
0470     emit closingApp();
0471 }
0472 
0473 PreferencesController* GameController::preferencesController() const
0474 {
0475     return m_preferencesDialogController.get();
0476 }
0477 void GameController::addCommand(QUndoCommand* cmd)
0478 {
0479     m_undoStack->push(cmd);
0480 }