gamehost.cpp
1 //------------------------------------------------------------------------------
2 // gamehost.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program 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
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; 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) 2013 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "gamehost.h"
24 
25 #include "configuration/doomseekerconfig.h"
26 #include "ini/inisection.h"
27 #include "ini/inivariable.h"
28 #include "plugins/engineplugin.h"
29 #include "serverapi/gamecreateparams.h"
30 #include "serverapi/message.h"
32 #include "apprunner.h"
33 #include "commandlinetokenizer.h"
34 #include "gamedemo.h"
35 #include <cassert>
36 #include <QFileInfo>
37 #include <QStringList>
38 
39 #define BAIL_ON_ERROR(method) \
40 { \
41  method; \
42  if (d->message.isError()) \
43  { \
44  return; \
45  } \
46 }
47 
48 DClass<GameHost>
49 {
50  public:
51  QString argBexLoading;
52  QString argDehLoading;
53  QString argIwadLoading;
54  QString argOptionalWadLoading;
55  QString argPort;
56  QString argPwadLoading;
57  QString argDemoPlayback;
58  QString argDemoRecord;
59  QString argServerLaunch;
60 
61  CommandLineInfo* currentCmdLine;
62  Message message;
63  GameCreateParams params;
64  EnginePlugin* plugin;
65 
66  void (GameHost::*addIwad)();
67  void (GameHost::*addPwads)();
68  void (GameHost::*addDMFlags)();
69  void (GameHost::*addGlobalGameCustomParameters)();
70 };
71 
72 DPointered(GameHost)
73 
75 {
76  d->argBexLoading = "-deh";
77  d->argDehLoading = "-deh";
78  d->argIwadLoading = "-iwad";
79  d->argOptionalWadLoading = "-file";
80  d->argPort = "-port";
81  d->argPwadLoading = "-file";
82  d->argDemoPlayback = "-playdemo";
83  d->argDemoRecord = "-record";
84  d->currentCmdLine = NULL;
85  d->plugin = plugin;
86 
87  set_addIwad(&GameHost::addIwad_default);
88  set_addPwads(&GameHost::addPwads_default);
89  set_addDMFlags(&GameHost::addDMFlags_default);
90  set_addGlobalGameCustomParameters(&GameHost::addGlobalGameCustomParameters_default);
91 }
92 
93 GameHost::~GameHost()
94 {
95 }
96 
97 POLYMORPHIC_DEFINE(void, GameHost, addIwad, (), ());
98 POLYMORPHIC_DEFINE(void, GameHost, addPwads, (), ());
99 POLYMORPHIC_DEFINE(void, GameHost, addDMFlags, (), ());
100 POLYMORPHIC_DEFINE(void, GameHost, addGlobalGameCustomParameters, (), ());
101 
103 {
104  args().append(params().customParameters());
105 }
106 
107 void GameHost::addDemoPlaybackIfApplicable()
108 {
109  if (params().hostMode() == GameCreateParams::Demo)
110  {
111  args() << argForDemoPlayback();
112  args() << params().demoPath();
113  }
114 }
115 
116 void GameHost::addDemoRecordIfApplicable()
117 {
118  if (params().hostMode() == GameCreateParams::Offline
119  && params().demoRecord() != GameDemo::NoDemo)
120  {
121  args() << argForDemoRecord();
122  args() << params().demoPath();
123  }
124 }
125 
126 void GameHost::addDMFlags_default()
127 {
128 }
129 
131 {
132 }
133 
134 void GameHost::addGlobalGameCustomParameters_default()
135 {
136  IniSection config = gConfig.iniSectionForPlugin(plugin());
137  QString customParameters = config["CustomParameters"];
138  CommandLineTokenizer tokenizer;
139  args() << tokenizer.tokenize(customParameters);
140 }
141 
142 void GameHost::addIwad_default()
143 {
144  const QString& iwadPath = params().iwadPath();
145 
146  if (iwadPath.isEmpty())
147  {
148  setMessage(Message::customError(tr("Iwad is not set")));
149  return;
150  }
151 
152  QFileInfo fi(iwadPath);
153 
154  if (!fi.isFile())
155  {
156  QString error = tr("Iwad Path error:\n\"%1\" doesn't exist or is a directory!").arg(iwadPath);
158  }
159 
160  args() << argForIwadLoading() << iwadPath;
161 }
162 
163 void GameHost::addPwads_default()
164 {
165  verifyPwadPaths();
166  for(int i = 0;i < params().pwadsPaths().size();++i)
167  {
168  const QString &pwad = params().pwadsPaths()[i];
169  args() << fileLoadingPrefix(i) << pwad;
170  }
171 }
172 
174 {
175  verifyPwadPaths();
176  QMap<QString, QStringList> groups;
177  for (int i = 0;i < params().pwadsPaths().size();++i)
178  {
179  const QString &pwad = params().pwadsPaths()[i];
180  QString prefix = fileLoadingPrefix(i);
181  groups[prefix] << pwad;
182  }
183  foreach (const QString &prefix, groups.keys())
184  {
185  args() << prefix;
186  foreach (const QString &file, groups[prefix])
187  {
188  args() << file;
189  }
190  }
191 }
192 
193 QString GameHost::fileLoadingPrefix(int index) const
194 {
195  const QString &pwad = params().pwadsPaths()[index];
196  bool optional = false;
197  if (params().pwadsOptional().size() > index)
198  {
199  optional = params().pwadsOptional()[index];
200  }
201 
202  if (pwad.toLower().endsWith(".deh"))
203  {
204  return argForDehLoading();
205  }
206  else if (pwad.toLower().endsWith(".bex"))
207  {
208  return argForBexLoading();
209  }
210 
211  if (optional)
212  return argForOptionalWadLoading();
213  return argForPwadLoading();
214 }
215 
216 const QString& GameHost::argForBexLoading() const
217 {
218  return d->argBexLoading;
219 }
220 
221 const QString& GameHost::argForDehLoading() const
222 {
223  return d->argDehLoading;
224 }
225 
226 const QString& GameHost::argForIwadLoading() const
227 {
228  return d->argIwadLoading;
229 }
230 
232 {
233  return d->argOptionalWadLoading;
234 }
235 
236 const QString& GameHost::argForPort() const
237 {
238  return d->argPort;
239 }
240 
241 const QString& GameHost::argForPwadLoading() const
242 {
243  return d->argPwadLoading;
244 }
245 
246 const QString& GameHost::argForDemoPlayback() const
247 {
248  return d->argDemoPlayback;
249 }
250 
251 const QString& GameHost::argForDemoRecord() const
252 {
253  return d->argDemoRecord;
254 }
255 
256 const QString& GameHost::argForServerLaunch() const
257 {
258  return d->argServerLaunch;
259 }
260 
261 QStringList &GameHost::args()
262 {
263  return d->currentCmdLine->args;
264 }
265 
267 {
268  BAIL_ON_ERROR(addGlobalGameCustomParameters());
269  BAIL_ON_ERROR(addIwad());
270  BAIL_ON_ERROR(addPwads());
271 
272  // Port
273  if (params().hostMode() == GameCreateParams::Host && params().port() > 0)
274  {
275  args() << argForPort() << QString::number(params().port());
276  }
277 
278  // CVars
279  const QList<GameCVar> &cvars = params().cvars();
280  foreach (const GameCVar &c, cvars)
281  {
282  args() << QString(c.command()) << c.valueString();
283  }
284 
285  if (params().hostMode() == GameCreateParams::Host)
286  {
287  // Some games may not offer such argument.
288  if (!argForServerLaunch().isEmpty())
289  {
290  args() << argForServerLaunch();
291  }
292  }
293 
294  BAIL_ON_ERROR(addDMFlags());
295  BAIL_ON_ERROR(addExtra());
296  BAIL_ON_ERROR(addCustomParameters());
297 
298  addDemoPlaybackIfApplicable();
299  addDemoRecordIfApplicable();
300  saveDemoMetaData();
301 }
302 
304 {
305  d->message = Message();
306  d->currentCmdLine = &cmdLine;
307  d->params = params;
308 
309  args().clear();
310 
311  setupGamePaths();
312  if (d->message.isError())
313  {
314  return d->message;
315  }
316 
318  return d->message;
319 }
320 
322 {
323  CommandLineInfo cmdLine;
324 
325  Message message = createHostCommandLine(params, cmdLine);
326  if (!message.isIgnore())
327  {
328  return message;
329  }
330 
331 #ifdef Q_OS_WIN32
332  const bool WRAP_IN_SSS_CONSOLE = false;
333 #else
334  const bool WRAP_IN_SSS_CONSOLE = params.hostMode() == GameCreateParams::Host;
335 #endif
336 
337  if (WRAP_IN_SSS_CONSOLE)
338  {
339  QIcon icon;
340  if (plugin() != NULL)
341  {
342  icon = plugin()->icon();
343  }
345  return Message();
346  }
347  else
348  {
349  return AppRunner::runExecutable(cmdLine);
350  }
351 }
352 
354 {
355  return d->params;
356 }
357 
359 {
360  return d->plugin;
361 }
362 
363 void GameHost::saveDemoMetaData()
364 {
365  if (params().demoRecord() == GameDemo::Managed)
366  {
367  GameDemo::saveDemoMetaData(params().demoPath(), *plugin(),
368  params().iwadName(), params().pwads());
369  }
370 }
371 
372 void GameHost::setArgForBexLoading(const QString& arg)
373 {
374  d->argBexLoading = arg;
375 }
376 
377 void GameHost::setArgForDehLoading(const QString& arg)
378 {
379  d->argDehLoading = arg;
380 }
381 
382 void GameHost::setArgForIwadLoading(const QString& arg)
383 {
384  d->argIwadLoading = arg;
385 }
386 
387 void GameHost::setArgForOptionalWadLoading(const QString& arg)
388 {
389  d->argOptionalWadLoading = arg;
390 }
391 
392 void GameHost::setArgForPort(const QString& arg)
393 {
394  d->argPort = arg;
395 }
396 
397 void GameHost::setArgForPwadLoading(const QString& arg)
398 {
399  d->argPwadLoading = arg;
400 }
401 
402 void GameHost::setArgForDemoPlayback(const QString& arg)
403 {
404  d->argDemoPlayback = arg;
405 }
406 
407 void GameHost::setArgForDemoRecord(const QString& arg)
408 {
409  d->argDemoRecord = arg;
410 }
411 
412 void GameHost::setArgForServerLaunch(const QString& arg)
413 {
414  d->argServerLaunch = arg;
415 }
416 
417 void GameHost::setMessage(const Message& message)
418 {
419  d->message = message;
420 }
421 
422 void GameHost::setupGamePaths()
423 {
424  QFileInfo fileInfo(params().executablePath());
425  if (!fileInfo.isFile() && !fileInfo.isBundle())
426  {
427  QString error = tr("%1\n doesn't exist or is not a file.").arg(fileInfo.filePath());
429  return;
430  }
431  d->currentCmdLine->executable = params().executablePath();
432  d->currentCmdLine->applicationDir = fileInfo.dir();
433 }
434 
436 {
437  foreach (const QString &pwad, params().pwadsPaths())
438  {
439  QFileInfo fi(pwad);
440  if (!fi.isFile())
441  {
442  QString error = tr("Pwad path error:\n\"%1\" doesn't exist or is a directory!").arg(pwad);
444  return false;
445  }
446  }
447  return true;
448 }
void addPwads_prefixOnce()
See: GameClientRunner::addModFiles_prefixOnce()
Definition: gamehost.cpp:173
EnginePlugin * plugin() const
EnginePlugin that this GameHost is associated with.
Definition: gamehost.cpp:358
Game parametrization data used when creating new games.
Structure holding parameters for application launch.
Definition: apprunner.h:37
Message object used to pass messages throughout the Doomseeker&#39;s system.
Definition: message.h:63
const QString & argForBexLoading() const
Command line parameter that is used to load a BEX file.
Definition: gamehost.cpp:216
const QString & argForDemoPlayback() const
Command line parameter for playing back a demo.
Definition: gamehost.cpp:246
const QString & argForPort() const
Command line parameter that is used to set network port for the game.
Definition: gamehost.cpp:236
const QString & argForServerLaunch() const
Command line parameter used to launch a server.
Definition: gamehost.cpp:256
void setMessage(const Message &message)
Call this method to convey errors.
Definition: gamehost.cpp:417
const QString & executablePath() const
Path to the game executable.
const QString & demoPath() const
Use if running in HostMode::Demo mode or recording a demo.
const GameCreateParams & params() const
GameCreateParams with which this game should be configured.
Definition: gamehost.cpp:353
const QString & argForOptionalWadLoading() const
Command line parameter that is used to load optional WADs.
Definition: gamehost.cpp:231
virtual void addCustomParameters()
"Custom parameters" are specified directly by user in "Create Game" dialog box.
Definition: gamehost.cpp:102
bool isIgnore() const
True for &#39;Null&#39; messages.
Definition: message.cpp:109
void addGlobalGameCustomParameters()
[Virtual] Adds custom parameters as defined in Doomseeker&#39;s configuration box.
void addDMFlags()
[Virtual] Creates engine specific command line parameters out of passed DM flags list.
const QString & argForPwadLoading() const
Command line parameter that is used to load a PWAD.
Definition: gamehost.cpp:241
void addPwads()
[Virtual] Loads PWADs and other mod files (dehacked patches, pk3s, etc.)
static void runExecutableWrappedInStandardServerConsole(const QIcon &icon, const CommandLineInfo &cli)
Executes predefined command line.
Definition: apprunner.cpp:116
QStringList & args()
Reference to command line arguments.
Definition: gamehost.cpp:261
virtual void createCommandLineArguments()
Builds command line arguments sequentially by calling other methods.
Definition: gamehost.cpp:266
bool verifyPwadPaths()
Verifies if all params().pwadsPaths() exist.
Definition: gamehost.cpp:435
Message createHostCommandLine(const GameCreateParams &params, CommandLineInfo &cmdLine)
Definition: gamehost.cpp:303
QList< GameCVar > & cvars()
Contents of this list will be passed as "+consoleCommand value" to the command line.
INI section representation.
Definition: inisection.h:40
Creates game servers, offline games or demo playbacks.
Definition: gamehost.h:69
Splits command line into separate arguments in a manner appropriate for current OS.
const QString & argForDemoRecord() const
Command line parameter for recording a demo.
Definition: gamehost.cpp:251
const QString & argForIwadLoading() const
Command line parameter that is used to set IWAD.
Definition: gamehost.cpp:226
Message host(const GameCreateParams &params)
Definition: gamehost.cpp:321
void addIwad()
[Virtual] Loads IWAD.
static Message customError(const QString &content)
Convenience method. Sets type to Type::CUSTOM_ERROR.
Definition: message.h:123
A general game setting or variable (like fraglimit).
const QString & command() const
Command-line argument that sets this GameCVar.
virtual void addExtra()
Creates engine specific command line parameters out of Server class fields.
Definition: gamehost.cpp:130
const QString & argForDehLoading() const
Command line parameter that is used to load a DEHACKED file.
Definition: gamehost.cpp:221