23 #include "joincommandlinebuilder.h"
25 #include "apprunner.h"
26 #include "datapaths.h"
28 #include "configuration/doomseekerconfig.h"
29 #include "gui/passworddlg.h"
30 #include "gui/wadseekerinterface.h"
31 #include "gui/wadseekershow.h"
32 #include "ini/settingsproviderqt.h"
33 #include "plugins/engineplugin.h"
34 #include "serverapi/exefile.h"
35 #include "serverapi/gameclientrunner.h"
36 #include "serverapi/message.h"
37 #include "serverapi/server.h"
38 #include "application.h"
41 #include <wadseeker/wadseeker.h>
42 #include <QDialogButtonBox>
43 #include <QGridLayout>
45 #include <QListWidget>
46 #include <QMessageBox>
49 DClass<JoinCommandLineBuilder>
53 bool configurationError;
54 QString connectPassword;
58 QString inGamePassword;
60 QWidget *parentWidget;
61 bool passwordsAlreadySet;
62 bool requireOptionals;
65 QDialogButtonBox *buttonBox;
66 QDialogButtonBox::StandardButton lastButtonClicked;
72 GameDemo demo, QWidget *parentWidget)
74 d->configurationError =
false;
76 d->demoName = GameDemo::mkDemoFullPath(demo, *server->plugin());
77 d->parentWidget = parentWidget;
78 d->passwordsAlreadySet =
false;
79 d->requireOptionals =
false;
83 JoinCommandLineBuilder::~JoinCommandLineBuilder()
87 void JoinCommandLineBuilder::allDownloadableWads(
const JoinError &joinError, QStringList &required, QStringList &optional)
94 const QList<PWad> missingWads = joinError.
missingWads();
95 foreach(
const PWad &wad, missingWads)
98 optional.append(wad.
name());
100 required.append(wad.
name());
102 required = Wadseeker::filterAllowedOnlyWads(required);
103 optional = Wadseeker::filterAllowedOnlyWads(optional);
108 if (d->server->isLockedAnywhere())
110 if (!d->passwordsAlreadySet)
113 int ret = password.exec();
114 if (ret != QDialog::Accepted)
118 d->connectPassword = password.connectPassword();
119 d->inGamePassword = password.inGamePassword();
120 d->passwordsAlreadySet =
true;
122 params.setConnectPassword(d->connectPassword);
123 params.setInGamePassword(d->inGamePassword);
126 if (!d->demoName.isEmpty())
128 params.setDemoName(d->demoName);
133 const CommandLineInfo &JoinCommandLineBuilder::builtCommandLine()
const
138 bool JoinCommandLineBuilder::checkServerStatus()
141 if (d->server->isRefreshing())
143 d->error = tr(
"This server is still refreshing.\nPlease wait until it is finished.");
144 gLog << tr(
"Attempted to obtain a join command line for a \"%1\" "
145 "server that is under refresh.").arg(d->server->addressWithPort());
149 else if (!d->server->isKnown())
151 d->error = tr(
"Data for this server is not available.\nOperation failed.");
152 gLog << tr(
"Attempted to obtain a join command line for an unknown server \"%1\"").arg(
153 d->server->addressWithPort());
159 bool JoinCommandLineBuilder::checkWadseekerValidity(QWidget *parent)
161 QString targetDirPath = gConfig.wadseeker.targetDirectory;
162 QDir targetDir(targetDirPath);
163 QFileInfo targetDirFileInfo(targetDirPath);
165 if (targetDirPath.isEmpty() || !targetDir.exists() || !targetDirFileInfo.isWritable())
173 int JoinCommandLineBuilder::displayMissingWadsMessage(
const QStringList &downloadableWads,
174 QStringList &optionalWads,
const QString &message)
176 const QString CAPTION = tr(
"Doomseeker - files are missing");
181 msgBox.setWindowTitle(CAPTION);
182 QGridLayout *grid =
new QGridLayout;
183 QLabel *mainMessage =
new QLabel;
184 grid->addWidget(mainMessage, 0, 1);
186 QListWidget *optionalList = NULL;
187 if(!optionalWads.isEmpty())
189 optionalList =
new QListWidget;
190 optionalList->setMaximumHeight(64);
191 grid->addWidget(optionalList, 1, 1);
192 foreach(
const QString &wad, optionalWads)
194 QListWidgetItem *item =
new QListWidgetItem(wad, optionalList);
195 item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
196 item->setCheckState(Qt::Checked);
200 QLabel *questionLabel =
new QLabel;
201 grid->addWidget(questionLabel, 2, 1);
206 d->buttonBox =
new QDialogButtonBox;
207 d->lastButtonClicked = QDialogButtonBox::NoButton;
208 grid->addWidget(d->buttonBox, 3, 0, 1, 2, Qt::AlignRight);
209 msgBox.connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
210 msgBox.connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
211 connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(missingWadsClicked(QAbstractButton*)));
213 QLabel *icon =
new QLabel;
214 QIcon questionIcon = msgBox.style()->standardIcon(QStyle::SP_MessageBoxQuestion);
215 icon->setPixmap(questionIcon.pixmap(questionIcon.actualSize(QSize(64,64))));
216 grid->addWidget(icon, 0, 0, 3, 1, Qt::AlignTop);
218 msgBox.setLayout(grid);
220 QString ignoreMessage;
221 if (d->server->plugin()->data()->inGameFileDownloads)
223 ignoreMessage = tr(
"Alternatively use ignore to connect anyways.");
224 d->buttonBox->addButton(QDialogButtonBox::Ignore);
227 QString questionMessage;
228 if (!downloadableWads.isEmpty() || !optionalWads.isEmpty())
230 questionLabel->setText(QString(
"%1\n%2").arg(tr(
"Do you want Wadseeker to find the missing WADs?")).arg(ignoreMessage));
231 d->buttonBox->addButton(QDialogButtonBox::Yes);
232 d->buttonBox->addButton(QDialogButtonBox::No);
233 mainMessage->setText(QString(
"%1\n\n%2").arg(message).arg(tr(
"Following files can be downloaded: %1").arg(downloadableWads.join(
", "))));
238 questionLabel->setText(ignoreMessage);
239 d->buttonBox->addButton(QDialogButtonBox::Ok);
240 mainMessage->setText(message);
246 for(
int i = optionalWads.size();i-- > 0;)
248 if(optionalList->item(i)->checkState() != Qt::Checked)
249 optionalWads.removeAt(i);
252 return d->lastButtonClicked;
255 const QString &JoinCommandLineBuilder::error()
const
260 void JoinCommandLineBuilder::failBuild()
263 emit commandLineBuildFinished();
266 void JoinCommandLineBuilder::handleError(
const JoinError &error)
268 if (!error.error().isEmpty())
270 d->error = error.error();
274 d->error = tr(
"Unknown error.");
276 d->configurationError = (error.type() == JoinError::ConfigurationError);
278 gLog << tr(
"Error when obtaining join parameters for server "
279 "\"%1\", game \"%2\": %3").arg(d->server->name()).arg(
280 d->server->engineName()).arg(d->error);
283 JoinCommandLineBuilder::MissingWadsProceed JoinCommandLineBuilder::handleMissingWads(
const JoinError &error)
285 if (WadseekerInterface::isInstantiated())
287 QMessageBox::StandardButtons ret =
288 QMessageBox::warning(d->parentWidget, tr(
"Doomseeker - files are missing"),
289 tr(
"You don't have all the files required by this server and an instance "
290 "of Wadseeker is already running.\n\n"
291 "Press 'Ignore' to join anyway."),
292 QMessageBox::Abort | QMessageBox::Ignore);
293 return ret == QMessageBox::Ignore ? Ignore : Cancel;;
296 QString filesMissingMessage = tr(
"Following files are missing:\n");
300 filesMissingMessage += tr(
"IWAD: ") + error.
missingIwad().toLower() +
"\n";
301 if (Wadseeker::isForbiddenWad(error.
missingIwad()))
303 filesMissingMessage += tr(
"\n"
304 "Make sure that this file is in one of the paths "
305 "specified in Options -> File Paths.\n"
306 "This file belongs to a commercial game or is otherwise "
307 "blocked from download. If you don't have this file, "
308 "and it belongs to a commercial game, "
309 "you need to purchase the game associated with this IWAD.\n"
310 "Wadseeker will not download commercial IWADs.\n\n");
316 const QList<PWad> missingWads = error.
missingWads();
318 QStringList optionals;
319 foreach(
const PWad &wad, missingWads)
322 optionals << wad.
name();
324 wadlist << wad.
name();
326 filesMissingMessage += tr(
"PWADS: %1").arg(wadlist.join(
", "));
327 if(!optionals.isEmpty())
329 filesMissingMessage +=
"\n";
330 filesMissingMessage += QString(
"Optional PWADS: %1").arg(optionals.join(
", "));
334 QStringList requiredDownloads, optionalDownloads;
335 allDownloadableWads(error, requiredDownloads, optionalDownloads);
336 QMessageBox::StandardButtons ret = (QMessageBox::StandardButtons)
337 displayMissingWadsMessage(requiredDownloads, optionalDownloads, filesMissingMessage);
338 if (ret == QMessageBox::Yes)
342 if ((requiredDownloads.isEmpty() && optionalDownloads.isEmpty())
343 || !gWadseekerShow->checkWadseekerValidity(d->parentWidget))
349 this->connect(wadseeker, SIGNAL(finished(
int)), SLOT(onWadseekerDone(
int)));
350 requiredDownloads.append(optionalDownloads);
351 wadseeker->
setWads(requiredDownloads);
352 wadseeker->setAttribute(Qt::WA_DeleteOnClose);
356 return ret == QMessageBox::Ignore ? Ignore : Cancel;
359 bool JoinCommandLineBuilder::isConfigurationError()
const
361 return d->configurationError;
364 void JoinCommandLineBuilder::missingWadsClicked(QAbstractButton *button)
366 d->lastButtonClicked = d->buttonBox->standardButton(button);
371 assert(d->server != NULL);
374 if (!checkServerStatus())
381 if (!buildServerConnectParams(params))
390 if(d->requireOptionals && joinError.type() == JoinError::NoError && !joinError.
missingWads().isEmpty())
391 joinError.setType(JoinError::MissingWads);
393 switch (joinError.type())
398 case JoinError::ConfigurationError:
399 case JoinError::Critical:
401 handleError(joinError);
408 if (tryToInstallGame())
419 case JoinError::MissingWads:
421 MissingWadsProceed proceed = handleMissingWads(joinError);
433 gLog <<
"Bug: not sure how to proceed after \"MissingWads\".";
440 case JoinError::NoError:
441 if (d->demo == GameDemo::Managed)
444 foreach (
const PWad &wad, d->server->wads())
448 GameDemo::saveDemoMetaData(d->demoName, *d->server->plugin(),
449 d->server->iwad(), pwads);
454 gLog <<
"JoinCommandLineBuilder - unhandled JoinError type!";
458 emit commandLineBuildFinished();
461 void JoinCommandLineBuilder::onWadseekerDone(
int result)
463 qDebug() <<
"onWadseekerDone:" << result;
464 if (result == QDialog::Accepted)
470 ServerPtr JoinCommandLineBuilder::server()
const
477 d->passwordsAlreadySet = !(connectPassword.isNull() && inGamePassword.isNull());
478 if(!connectPassword.isNull())
479 d->connectPassword = connectPassword;
480 if(!inGamePassword.isNull())
481 d->inGamePassword = inGamePassword;
486 d->requireOptionals = required;
489 bool JoinCommandLineBuilder::tryToInstallGame()
491 Message message = d->server->clientExe()->install(gApp->mainWindowAsQWidget());
494 QMessageBox::critical(gApp->mainWindowAsQWidget(), tr(
"Game installation failure"),
495 message.
contents(), QMessageBox::Ok);
Structure holding parameters for application launch.
Message object used to pass messages throughout the Doomseeker's system.
A DTO for GameClientRunner; exchanges information between main program and plugins, and allows future extensions.
bool isError() const
True if type() is equal to or greater than CUSTOM_ERROR.
void setPasswords(const QString &connectPassword=QString(), const QString &inGamePassword=QString())
Sets the connect/ingame password and bypasses the prompt. Set passwords to a null string to unset...
const QList< PWad > & missingWads() const
Generates command line for joining specified server.
Aborts without printing error.
Indicator of error for the server join process.
void setRequireOptionals(bool)
Treats optional wads are required so Wadseeker prompts.
JoinError createJoinCommandLine(CommandLineInfo &cli, const ServerConnectParams ¶ms)
Fills out CommandLineInfo object that allows client executables to be launched.
const QString & missingIwad() const
void obtainJoinCommandLine()
Runs asynchronously and emits commandLineBuildFinished() when done.
void setWads(const QStringList &wads)
Sets WADs to seek.
Wadseeker dialog box, only one instance is allowed.
Game executable was not found but it can be automatically installed by the plugin.
bool isOptional() const
Is this WAD required to join the server?
static const unsigned SUCCESSFUL
Message indicates that the operation was successful.
unsigned type() const
Message::Type.
QString contents() const
Customized displayable contents of this Message.
Creates command line that launches the client executable of the game and connects it to a server...
const QString & name() const
File name of the WAD.