24 #include <QApplication> 27 #include <QHashIterator> 29 #include <QMainWindow> 30 #include <QMessageBox> 32 #include <QSslConfiguration> 33 #include <QThreadPool> 38 #include "configuration/doomseekerconfig.h" 39 #include "configuration/passwordscfg.h" 40 #include "configuration/queryspeed.h" 41 #include "connectionhandler.h" 42 #include "gui/createserverdialog.h" 43 #include "gui/mainwindow.h" 44 #include "gui/remoteconsole.h" 45 #include "ip2c/ip2c.h" 47 #include "irc/configuration/ircconfig.h" 48 #include "serverapi/server.h" 49 #include "application.h" 50 #include "cmdargshelp.h" 51 #include "commandlinetokenizer.h" 52 #include "datapaths.h" 53 #include "doomseekerfilepaths.h" 54 #include "localization.h" 57 #include "plugins/engineplugin.h" 58 #include "plugins/pluginloader.h" 59 #include "refresher/refresher.h" 60 #include "serverapi/server.h" 61 #include "strings.hpp" 62 #include "tests/testruns.h" 63 #include "wadseeker/wadseeker.h" 64 #include "updater/updateinstaller.h" 65 #include "versiondump.h" 67 QString Main::argDataDir;
71 Main::Main(
int argc,
char* argv[])
72 : arguments(argv), argumentsCount(argc),
73 startCreateGame(false), startRcon(false)
77 bPortableMode =
false;
79 logVerbosity = LV_Default;
82 qRegisterMetaType<ServerPtr>(
"ServerPtr");
83 qRegisterMetaType<ServerCPtr>(
"ServerCPtr");
88 if (Application::isInit())
93 if (Refresher::isInstantiated())
95 Refresher::instance()->
quit();
96 Refresher::deinstantiate();
100 if (Application::isInit())
102 gConfig.saveToFile();
105 gIRCConfig.saveToFile();
106 gIRCConfig.dispose();
109 IP2C::deinstantiate();
115 int Main::connectToServerByURL()
121 connect(handler, SIGNAL(finished(
int)), gApp, SLOT(quit()));
123 int ret = gApp->exec();
134 if (!interpretCommandLineParameters())
140 Application::init(argumentsCount, arguments);
143 gApp->setAttribute(Qt::AA_DontShowIconsInMenus);
146 gLog <<
"Starting Doomseeker. Hello World! :)";
147 gLog <<
"Setting up data directories.";
149 if (!initDataDirectories())
157 return runVersionDump();
159 PluginUrlHandler::registerAll();
163 return runTestMode();
167 #ifdef WITH_AUTOUPDATES 177 initLocalizationsDefinitions();
179 initPasswordsConfig();
185 QTimer::singleShot(0,
this, SLOT(runCreateGame()));
189 QTimer::singleShot(0,
this, SLOT(runRemoteConsole()));
191 else if (connectUrl.isValid())
193 setupRefreshingThread();
194 return connectToServerByURL();
198 setupRefreshingThread();
200 #ifdef WITH_AUTOUPDATES 203 if (updateFailedCode != 0)
206 gApp->mainWindow()->setDisplayUpdaterProcessFailure(updateFailedCode);
211 gApp->mainWindow()->setDisplayUpdateInstallerError(updateInstallerResult);
217 QTimer::singleShot(0, gApp->mainWindow(), SLOT(checkForUpdatesAuto()));
223 gLog << tr(
"Init finished.");
224 gLog.addUnformattedEntry(
"================================\n");
226 int returnCode = gApp->exec();
228 #ifdef WITH_AUTOUPDATES 229 if (bInstallUpdatesAndRestart)
233 updateFailedCode = 0;
234 int installResult = installPendingUpdates();
238 QMessageBox::critical(NULL, tr(
"Doomseeker - Updates Install Failure"),
247 int Main::runTestMode()
250 gLog <<
"Entering test mode.";
255 TestRuns::pTestCore = &testCore;
256 TestRuns::callTests();
259 QString strSucceded =
"Tests succeeded: %1";
260 QString strFailed =
"Tests failed: %1";
261 QString strPercentage =
"Pass percentage: %1%";
263 float passPercentage = (float)testCore.numTestsSucceeded() / (float)testCore.numTests();
264 passPercentage *= 100.0f;
266 gLog <<
"==== TESTS SUMMARY: ====";
267 gLog << strSucceded.arg(testCore.numTestsSucceeded(), 6);
268 gLog << strFailed.arg(testCore.numTestsFailed(), 6);
269 gLog << strPercentage.arg(passPercentage, 6,
'f', 2);
270 gLog <<
"==== Done. ====";
272 return testCore.numTestsFailed();
275 int Main::runVersionDump()
279 if (!versionDumpFile.isEmpty())
281 outfile.setFileName(versionDumpFile);
282 if (!outfile.open(QIODevice::WriteOnly | QIODevice::Text))
284 error = tr(
"Failed to open file '%1'.").arg(versionDumpFile);
290 if (!outfile.open(stdout, QIODevice::WriteOnly))
292 error = tr(
"Failed to open stdout.");
295 if (!error.isEmpty())
297 gLog.setPrintingToStderr(
true);
302 gLog << tr(
"Dumping version info to file in JSON format.");
303 VersionDump::dumpJsonToIO(outfile);
307 void Main::applyLogVerbosity()
309 gLog.setPrintingToStderr(shouldLogToStderr());
312 void Main::createMainWindow()
314 gLog << tr(
"Preparing GUI.");
316 gApp->setMainWindow(
new MainWindow(gApp, argumentsCount, arguments));
317 gApp->mainWindow()->show();
321 gApp->mainWindow()->notifyFirstRun();
325 void Main::runCreateGame()
327 gLog << tr(
"Starting Create Game box.");
329 dialog->setConfigureButtonVisible(
true);
330 dialog->setWindowIcon(Application::icon());
334 void Main::runRemoteConsole()
336 gLog << tr(
"Starting RCon client.");
337 if(rconPluginName.isEmpty())
339 bool canAnyEngineRcon =
false;
340 for(
unsigned int i = 0;i < gPlugins->numPlugins();i++)
343 if (info->
server(QHostAddress(
"localhost"), 0)->hasRcon())
345 canAnyEngineRcon =
true;
349 if (!canAnyEngineRcon)
351 QString error = tr(
"None of the currently loaded game plugins supports RCon.");
353 QMessageBox::critical(NULL, tr(
"Doomseeker RCon"), error);
364 int pIndex = gPlugins->pluginIndexFromName(rconPluginName);
367 gLog << tr(
"Couldn't find specified plugin: ") + rconPluginName;
373 const EnginePlugin *plugin = gPlugins->plugin(pIndex)->info();
374 ServerPtr server = plugin->
server(QHostAddress(rconAddress), rconPort);
375 if(!server->hasRcon())
377 gLog << tr(
"Plugin does not support RCon.");
388 void Main::initCaCerts()
390 QString certsFilePath = DoomseekerFilePaths::cacerts();
391 QFile certsFile(certsFilePath);
392 if (!certsFilePath.isEmpty() && certsFile.exists())
394 gLog << tr(
"Loading extra CA certificates from '%1'.").arg(certsFilePath);
395 certsFile.open(QIODevice::ReadOnly);
396 QSslConfiguration sslConf = QSslConfiguration::defaultConfiguration();
397 QList<QSslCertificate> cacerts = sslConf.caCertificates();
398 QList<QSslCertificate> extraCerts = QSslCertificate::fromDevice(&certsFile);
399 gLog << tr(
"Appending %n extra CA certificate(s).", NULL, extraCerts.size());
400 cacerts.append(extraCerts);
401 sslConf.setCaCertificates(cacerts);
402 QSslConfiguration::setDefaultConfiguration(sslConf);
407 bool Main::initDataDirectories()
409 DataPaths::initDefault(bPortableMode);
410 DoomseekerFilePaths::pDataPaths = gDefaultDataPaths;
411 QList<DataPaths::DirErrno> failedDirsErrno = gDefaultDataPaths->createDirectories();
412 if (!failedDirsErrno.isEmpty())
416 QString errorMessage = tr(
"Doomseeker will not run because some directories cannot be used properly.\n");
419 errorMessage +=
"\n[" + QString::number(failedDirErrno.errnoNum) +
"] ";
420 errorMessage += failedDirErrno.directory.absolutePath() +
": ";
421 errorMessage += failedDirErrno.errnoString;
424 QMessageBox::critical(NULL, tr(
"Doomseeker startup error"), errorMessage);
430 dataDirectories << gDefaultDataPaths->localDataLocationPath();
431 dataDirectories << gDefaultDataPaths->workingDirectory();
434 dataDirectories <<
"./";
435 #if defined(Q_OS_LINUX) 437 dataDirectories << INSTALL_PREFIX
"/share/doomseeker/";
440 dataDirectories <<
":/";
441 QDir::setSearchPaths(
"data", dataDirectories);
448 gLog << tr(
"Initializing IP2C database.");
454 void Main::initIRCConfig()
456 gLog << tr(
"Initializing IRC configuration file.");
462 QString configPath = DoomseekerFilePaths::ircIni();
463 if (!configPath.isEmpty())
465 if (gIRCConfig.setIniFile(configPath))
467 gIRCConfig.readFromFile();
472 void Main::initLocalizationsDefinitions()
474 gLog << tr(
"Loading translations definitions");
475 Localization::get()->loadLocalizationsList(
479 gConfig.doomseeker.localization);
482 gLog << tr(
"Loading translation \"%1\".").arg(bestMatchedLocalization.
localeName);
483 bool bSuccess = Localization::get()->loadTranslation(bestMatchedLocalization.
localeName);
486 gLog << tr(
"Translation loaded.");
490 gLog << tr(
"Failed to load translation.");
495 void Main::initMainConfig()
497 gLog << tr(
"Initializing configuration file.");
503 QString configDirPath = gDefaultDataPaths->programsDataDirectoryPath();
504 if (configDirPath.isEmpty())
506 gLog << tr(
"Could not get an access to the settings directory. Configuration will not be saved.");
510 QString filePath = DoomseekerFilePaths::ini();
513 QFileInfo iniFileInfo(filePath);
514 bIsFirstRun = !iniFileInfo.exists();
517 if (gConfig.setIniFile(filePath))
519 gConfig.readFromFile();
523 void Main::initPasswordsConfig()
525 gLog << tr(
"Initializing passwords configuration file.");
527 QString configDirPath = gDefaultDataPaths->programsDataDirectoryPath();
528 if (configDirPath.isEmpty())
532 QString filePath = DoomseekerFilePaths::passwordIni();
533 PasswordsCfg::initIni(filePath);
536 void Main::initPluginConfig()
538 gLog << tr(
"Initializing configuration for plugins.");
539 gPlugins->initConfig();
542 int Main::installPendingUpdates()
545 if (gConfig.autoUpdates.bPerformUpdateOnNextRun)
547 gConfig.autoUpdates.bPerformUpdateOnNextRun =
false;
548 gConfig.saveToFile();
551 if (updateFailedCode == 0)
557 return updateInstallerResult;
560 bool Main::interpretCommandLineParameters()
564 for (
int i = 1; i < argumentsCount && failure.isEmpty(); ++i)
566 const char* arg = arguments[i];
568 if (strcmp(arg,
"--connect") == 0)
570 if (i + 1 < argumentsCount)
573 connectUrl = QUrl(arguments[i]);
581 else if (strcmp(arg,
"--create-game") == 0)
583 startCreateGame =
true;
585 else if (strcmp(arg,
"--datadir") == 0)
587 if (i + 1 < argumentsCount)
590 dataDirectories.prepend(arguments[i]);
591 argDataDir = arguments[i];
598 else if (strcmp(arg,
"--rcon") == 0)
601 if (i + 2 < argumentsCount)
603 rconPluginName = arguments[++i];
607 else if (strcmp(arg,
"--help") == 0)
609 gLog.setTimestampsEnabled(
false);
614 else if (strcmp(arg,
"--update-failed") == 0)
617 updateFailedCode = QString(arguments[i]).toInt();
619 else if (strcmp(arg,
"--portable") == 0)
621 bPortableMode =
true;
623 else if (strcmp(arg,
"--quiet") == 0)
625 logVerbosity = LV_Quiet;
627 else if (strcmp(arg,
"--tests") == 0)
631 else if (strcmp(arg,
"--verbose") == 0)
633 logVerbosity = LV_Verbose;
635 else if (strcmp(arg,
"--version-json") == 0)
638 if (i + 1 < argumentsCount)
641 QString filename = arguments[i];
642 if (filename !=
"-" && filename !=
"")
644 versionDumpFile = filename;
654 QList<bool> exclusives;
655 exclusives << !connectUrl.isEmpty() << startCreateGame << startRcon;
656 if (exclusives.count(
true) > 1)
657 failure = tr(
"doomseeker: `--connect`, `--create-game` and `--rcon` are mutually exclusive");
659 if (!failure.isEmpty())
661 gLog.setTimestampsEnabled(
false);
668 void Main::setupRefreshingThread()
670 gLog << tr(
"Starting refreshing thread.");
671 gRefresher->setDelayBetweenResends(gConfig.doomseeker.querySpeed().delayBetweenSingleServerAttempts);
675 bool Main::shouldLogToStderr()
const 678 return logVerbosity != LV_Quiet;
680 return logVerbosity == LV_Verbose;
681 return logVerbosity != LV_Quiet;
688 #define USE_WINMAIN_AS_ENTRY_POINT 692 #ifdef USE_WINMAIN_AS_ENTRY_POINT 694 QStringList getCommandLineArgs()
697 return tokenizer.tokenize(QString::fromUtf16((
const ushort*)GetCommandLineW()));
700 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine,
int nCmdShow)
705 QStringList commandLine = getCommandLineArgs();
708 argc = commandLine.size();
709 argv =
new char*[argc];
711 for (
int i = 0; i < commandLine.size(); ++i)
713 const QString& parameter = commandLine[i];
714 argv[i] =
new char[parameter.toUtf8().size() + 1];
715 strcpy(argv[i], parameter.toUtf8().constData());
719 int returnValue = pMain->run();
726 for (
int i = 0; i < argc; ++i)
735 int main(
int argc,
char* argv[])
738 int returnValue = pMain->
run();
static void translateServerAddress(const QString &addressString, QString &hostname, unsigned short &port, const QString &defaultAddress)
Translates string in format "hostname:port" to atomic values.
static QString unrecognizedOption(QString option)
Returns a string informing about the use of an unknown option, followed by availableCommands().
QString localeName
Compliant with language_country standard. See QLocale::name()
LocalizationInfo coerceBestMatchingLocalization(const QString &localeName) const
static void deinit()
Destroys the init() instance.
static void init(const QStringList &directories)
Attempts to load plugins from given set of directories.
static QString availableCommands()
Prepends "Available command line parameters" to argsHelp().
static bool bInstallUpdatesAndRestart
If true then program will install updates and restart instead of quitting if quit is requested...
static QString missingArgs(int expectedArguments, QString option)
Returns a string informing about the lack of arguments, followed by availableCommands().
int run()
Replaces main().
virtual ServerPtr server(const QHostAddress &address, unsigned short port) const
Creates an instance of server object from this plugin.
Dialog window allowing user to create a game.
Struct which contains the relevant QDir, the errno reported, and the QString generated by the errno...
Splits command line into separate arguments in a manner appropriate for current OS.
ErrorCode startInstallation()
Starts update process.
static void deinit()
Deinitializes the program; executed when program is shutting down.
Core for developer tests.
static const LocalizationInfo PROGRAM_NATIVE
static QStringList staticDataSearchDirs(const QString &subdir=QString())
Paths to directories where program should search for its static data.