generalgamesetuppanel.cpp
1 //------------------------------------------------------------------------------
2 // generalgamesetuppanel.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2014 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "generalgamesetuppanel.h"
24 #include "ui_generalgamesetuppanel.h"
25 
26 #include "configuration/doomseekerconfig.h"
27 #include "filefilter.h"
28 #include "gui/createserver/maplistpanel.h"
29 #include "gui/createserverdialog.h"
30 #include "ini/ini.h"
31 #include "plugins/engineplugin.h"
32 #include "plugins/pluginloader.h"
33 #include "serverapi/gamecreateparams.h"
34 #include "serverapi/gameexefactory.h"
35 #include "serverapi/gamefile.h"
36 #include <cassert>
37 #include <QFileDialog>
38 #include <QFileInfo>
39 #include <QMessageBox>
40 #include <QTimer>
41 
42 DClass<GeneralGameSetupPanel> : public Ui::GeneralGameSetupPanel
43 {
44 public:
45  EnginePlugin *currentEngine;
46  GameCreateParams::HostMode hostMode;
47  bool iwadSetExplicitly;
48  CreateServerDialog *parentDialog;
49 
50  int serverNameRow;
51  int portRow;
52  int hostModeSpacerRow;
53 
54  bool isValidExecutable(const QString &executablePath)
55  {
56  QFileInfo fileInfo(executablePath);
57  return !executablePath.isEmpty() && fileInfo.isFile();
58  }
59 };
60 
61 DPointered(GeneralGameSetupPanel)
62 
63 
65  : QWidget(parent)
66 {
67  d->setupUi(this);
68  d->hostMode = GameCreateParams::Host;
69  d->iwadSetExplicitly = false;
70  d->parentDialog = nullptr;
71 
72  d->hostExecutableInput->setAllowedExecutables(GameFile::Server);
73  d->offlineExecutableInput->setAllowedExecutables(GameFile::Offline);
74  d->remoteExecutableInput->setAllowedExecutables(GameFile::Client);
75 
76  d->formLayout->getWidgetPosition(d->leServerName, &d->serverNameRow, nullptr);
77  d->formLayout->getWidgetPosition(d->portArea, &d->portRow, nullptr);
78  d->formLayout->getWidgetPosition(d->hostModeSpacer, &d->hostModeSpacerRow, nullptr);
79 
80  this->connect(d->cboEngine, SIGNAL(currentPluginChanged(EnginePlugin*)),
81  SIGNAL(pluginChanged(EnginePlugin*)));
82 }
83 
84 GeneralGameSetupPanel::~GeneralGameSetupPanel()
85 {
86 }
87 
88 QStringList GeneralGameSetupPanel::getAllWadPaths() const
89 {
90  QStringList paths;
91  paths << d->iwadPicker->currentIwad() << d->wadsPicker->filePaths();
92  return paths;
93 }
94 
95 void GeneralGameSetupPanel::fillInParams(GameCreateParams &params)
96 {
97  params.setExecutablePath(pathToExe());
98  params.setIwadPath(d->iwadPicker->currentIwad());
99  params.setLoggingPath(d->logDirectoryPicker->validatedCurrentPath());
100  params.setPwadsPaths(d->wadsPicker->filePaths());
101  params.setPwadsOptional(d->wadsPicker->fileOptional());
102  params.setBroadcastToLan(d->cbBroadcastToLAN->isChecked());
103  params.setBroadcastToMaster(d->cbBroadcastToMaster->isChecked());
104  params.setMap(d->leMap->text());
105  params.setName(d->leServerName->text());
106  params.setPort(d->spinPort->isEnabled() ? d->spinPort->value() : 0);
107  params.setGameMode(currentGameMode());
108  params.setSkill(d->cboDifficulty->itemData(d->cboDifficulty->currentIndex()).toInt());
109  params.setUpnp(d->cbUpnp->isChecked());
110  params.setUpnpPort(d->spinUpnpPort->value());
111 }
112 
113 void GeneralGameSetupPanel::loadConfig(Ini &config, bool loadingPrevious)
114 {
115  IniSection general = config.section("General");
116 
117  // Engine
118  const EnginePlugin *prevEngine = d->currentEngine;
119  QString configEngineName = general["engine"];
120  bool engineChanged = false;
121  if (d->hostMode != GameCreateParams::Remote)
122  {
123  if (!setEngine(configEngineName))
124  return;
125  engineChanged = (prevEngine != d->currentEngine);
126  }
127 
128  // Executables
129  bool changeExecutable = engineChanged || !d->cbLockExecutable->isChecked();
130  if (d->hostMode == GameCreateParams::Remote)
131  {
132  // If we're configuring a remote game the engine cannot be changed.
133  // Don't substitute the executable when the config that is being
134  // loaded is for a different game.
135  auto *plugin = gPlugins->plugin(gPlugins->pluginIndexFromName(configEngineName));
136  if (plugin == nullptr || d->currentEngine != plugin->info())
137  changeExecutable = false;
138  }
139 
140  if (changeExecutable)
141  {
142  QString hostExecutablePath = *general["hostExecutable"];
143  if (d->isValidExecutable(hostExecutablePath))
144  d->hostExecutableInput->setPath(hostExecutablePath);
145 
146  QString offlineExecutablePath = *general["offlineExecutable"];
147  if (d->isValidExecutable(offlineExecutablePath))
148  d->offlineExecutableInput->setPath(offlineExecutablePath);
149 
150  QString remoteExecutablePath = *general["remoteExecutable"];
151  if (d->isValidExecutable(remoteExecutablePath))
152  d->remoteExecutableInput->setPath(remoteExecutablePath);
153  }
154 
155  // Other
156  d->leServerName->setText(general["name"]);
157  d->spinPort->setValue(general["port"]);
158 
159  int gameModeIndex = d->cboGamemode->findData(static_cast<gamemode_id>(general["gamemode"]));
160  if (gameModeIndex >= 0)
161  d->cboGamemode->setCurrentIndex(gameModeIndex);
162 
163  int difficultyIndex = d->cboDifficulty->findData(static_cast<int>(general["difficulty"]));
164  d->cboDifficulty->setCurrentIndex(qMax(0, difficultyIndex));
165 
166  d->leMap->setText(general["map"]);
167 
168  if (!(loadingPrevious && d->iwadSetExplicitly))
169  d->iwadPicker->addIwad(general["iwad"]);
170 
171  d->logDirectoryPicker->setLoggingEnabled(general["logEnabled"]);
172  d->logDirectoryPicker->setPathAndUpdate(general["logDir"]);
173 
174  QList<bool> optionalWads;
175  for (QString value : general["pwadsOptional"].valueString().split(";"))
176  {
177  optionalWads << (value != "0");
178  }
179  d->wadsPicker->setFilePaths(general["pwads"].valueString().split(";"), optionalWads);
180 
181  d->cbBroadcastToLAN->setChecked(general["broadcastToLAN"]);
182  d->cbBroadcastToMaster->setChecked(general["broadcastToMaster"]);
183  d->cbUpnp->setChecked(general["upnp"]);
184  d->spinUpnpPort->setValue(general["upnpPort"]);
185 
186  // Timer triggers slot after config is fully loaded.
187  QTimer::singleShot(0, this, SLOT(updateMapWarningVisibility()));
188 }
189 
190 void GeneralGameSetupPanel::saveConfig(Ini &config)
191 {
192  IniSection general = config.section("General");
193  general["engine"] = d->cboEngine->currentText();
194  general["hostExecutable"] = d->hostExecutableInput->path();
195  general["offlineExecutable"] = d->offlineExecutableInput->path();
196  general["remoteExecutable"] = d->remoteExecutableInput->path();
197  general["name"] = d->leServerName->text();
198  general["port"] = d->spinPort->value();
199  general["logDir"] = d->logDirectoryPicker->currentPath();
200  general["logEnabled"] = d->logDirectoryPicker->isLoggingEnabled();
201  general["gamemode"] = d->cboGamemode->itemData(d->cboGamemode->currentIndex()).toInt();
202  general["map"] = d->leMap->text();
203  general["difficulty"] = d->cboDifficulty->itemData(d->cboDifficulty->currentIndex()).toInt();
204  general["iwad"] = d->iwadPicker->currentIwad();
205 
206  general["pwads"] = d->wadsPicker->filePaths().join(";");
207  QList<bool> optionalWads = d->wadsPicker->fileOptional();
208  QStringList optionalList;
209  for (bool optional : optionalWads)
210  {
211  optionalList << (optional ? "1" : "0");
212  }
213  general["pwadsOptional"] = optionalList.join(";");
214 
215  general["broadcastToLAN"] = d->cbBroadcastToLAN->isChecked();
216  general["broadcastToMaster"] = d->cbBroadcastToMaster->isChecked();
217  general["upnp"] = d->cbUpnp->isChecked();
218  general["upnpPort"] = d->spinUpnpPort->value();
219 }
220 
221 void GeneralGameSetupPanel::reloadAppConfig()
222 {
223  d->hostExecutableInput->reloadExecutables();
224  d->offlineExecutableInput->reloadExecutables();
225  d->remoteExecutableInput->reloadExecutables();
226  d->iwadPicker->loadIwads();
227 }
228 
229 void GeneralGameSetupPanel::setupDifficulty(const EnginePlugin *engine)
230 {
231  QVariant oldDifficulty = d->cboDifficulty->itemData(d->cboDifficulty->currentIndex());
232  d->cboDifficulty->clear();
233 
234  QList<GameCVar> levels = engine->data()->difficulty->get(QVariant());
235  d->labelDifficulty->setVisible(!levels.isEmpty());
236  d->cboDifficulty->setVisible(!levels.isEmpty());
237  d->cboDifficulty->addItem(tr("< NONE >"), Skill::UNDEFINED);
238  for (const GameCVar &level : levels)
239  {
240  d->cboDifficulty->addItem(level.name(), level.value());
241  }
242  int memorizedIndex = d->cboDifficulty->findData(oldDifficulty);
243  if (memorizedIndex >= 0)
244  d->cboDifficulty->setCurrentIndex(memorizedIndex);
245 }
246 
247 void GeneralGameSetupPanel::setupForEngine(EnginePlugin *engine)
248 {
249  d->currentEngine = engine;
250 
251  d->cboEngine->setPluginByName(engine->data()->name);
252  d->labelIwad->setVisible(engine->data()->hasIwad);
253  d->iwadPicker->setVisible(engine->data()->hasIwad);
254  d->labelLogging->setVisible(engine->data()->allowsLogging);
255  d->logDirectoryPicker->setVisible(engine->data()->allowsLogging);
256  d->upnpArea->setVisible(engine->data()->allowsUpnp);
257  d->spinUpnpPort->setVisible(engine->data()->allowsUpnpPort);
258 
259  d->hostExecutableInput->setPlugin(engine);
260  d->offlineExecutableInput->setPlugin(engine);
261  d->remoteExecutableInput->setPlugin(engine);
262 
263  d->spinPort->setValue(d->currentEngine->data()->defaultServerPort);
264 
265  d->cboGamemode->clear();
266  d->cboGamemode->addItem(tr("< NONE >"), GameMode::SGM_Unknown);
267 
268  QList<GameMode> gameModes = d->currentEngine->gameModes();
269  for (int i = 0; i < gameModes.count(); ++i)
270  d->cboGamemode->addItem(gameModes[i].name(), gameModes[i].index());
271  setupDifficulty(engine);
272 }
273 
274 void GeneralGameSetupPanel::setupForHostMode(GameCreateParams::HostMode hostMode)
275 {
276  d->hostMode = hostMode;
277 
278  d->cboEngine->setDisabled(hostMode == GameCreateParams::Remote);
279 
280  d->hostExecutableInput->hide();
281  d->offlineExecutableInput->hide();
282  d->remoteExecutableInput->hide();
283  switch (hostMode)
284  {
285  default:
286  case GameCreateParams::Host: d->hostExecutableInput->show(); break;
287  case GameCreateParams::Offline: d->offlineExecutableInput->show(); break;
288  case GameCreateParams::Remote: d->remoteExecutableInput->show(); break;
289  }
290 
291  d->labelServerName->setVisible(hostMode == GameCreateParams::Host);
292  d->leServerName->setVisible(hostMode == GameCreateParams::Host);
293  d->labelPort->setVisible(hostMode == GameCreateParams::Host);
294  d->portArea->setVisible(hostMode == GameCreateParams::Host);
295  d->hostModeSpacer->setVisible(hostMode == GameCreateParams::Host);
296  d->broadcastOptionsArea->setVisible(hostMode == GameCreateParams::Host);
297 
298  // Insert/remove operations are necessary to get rid of extra
299  // spacing remaining when hiding form elements.
300  if (hostMode == GameCreateParams::Host)
301  {
302  d->formLayout->insertRow(d->serverNameRow, d->labelServerName, d->leServerName);
303  d->formLayout->insertRow(d->portRow, d->labelPort, d->portArea);
304  d->formLayout->insertRow(d->hostModeSpacerRow, d->hostModeSpacer,
305  static_cast<QWidget *>(nullptr));
306  }
307  else
308  {
309  d->formLayout->removeWidget(d->labelServerName);
310  d->formLayout->removeWidget(d->leServerName);
311  d->formLayout->removeWidget(d->labelPort);
312  d->formLayout->removeWidget(d->portArea);
313  d->formLayout->removeWidget(d->hostModeSpacer);
314  }
315 }
316 
317 void GeneralGameSetupPanel::setCreateServerDialog(CreateServerDialog *dialog)
318 {
319  d->parentDialog = dialog;
320 }
321 
322 void GeneralGameSetupPanel::setIwadByName(const QString &iwad)
323 {
324  d->iwadSetExplicitly = true;
325  d->iwadPicker->setIwadByName(iwad);
326 }
327 
328 void GeneralGameSetupPanel::showEvent(QShowEvent *event)
329 {
330  Q_UNUSED(event);
331  updateMapWarningVisibility();
332 }
333 
334 QString GeneralGameSetupPanel::mapName() const
335 {
336  return d->leMap->text();
337 }
338 
339 QString GeneralGameSetupPanel::pathToExe()
340 {
341  switch (d->hostMode)
342  {
343  default:
344  case GameCreateParams::Host: return d->hostExecutableInput->path();
345  case GameCreateParams::Offline: return d->offlineExecutableInput->path();
346  case GameCreateParams::Remote: return d->remoteExecutableInput->path();
347  }
348 }
349 
350 void GeneralGameSetupPanel::onGameModeChanged(int index)
351 {
352  if (index >= 0)
353  emit gameModeChanged(currentGameMode());
354 }
355 
356 GameMode GeneralGameSetupPanel::currentGameMode() const
357 {
358  QList<GameMode> gameModes = d->currentEngine->gameModes();
359  for (const GameMode &mode : gameModes)
360  {
361  if (mode.index() == d->cboGamemode->itemData(d->cboGamemode->currentIndex()).toInt())
362  return mode;
363  }
364  return GameMode::mkUnknown();
365 }
366 
367 EnginePlugin *GeneralGameSetupPanel::currentPlugin() const
368 {
369  return d->cboEngine->currentPlugin();
370 }
371 
372 bool GeneralGameSetupPanel::setEngine(const QString &engineName)
373 {
374  if (!d->cboEngine->setPluginByName(engineName))
375  {
376  QMessageBox::critical(this, tr("Doomseeker - load server config"),
377  tr("Plugin for engine \"%1\" is not present!").arg(engineName));
378  return false;
379  }
380  return true;
381 }
382 
383 void GeneralGameSetupPanel::updateMapWarningVisibility()
384 {
385  assert(d->parentDialog != nullptr);
386  MapListPanel *mapList = d->parentDialog->mapListPanel();
387  d->lblMapWarning->setVisible(mapList->hasMaps() && !mapList->isMapOnList(mapName()));
388 }