engineplugin.cpp
1 //------------------------------------------------------------------------------
2 // engineplugin.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) 2011 Braden "Blzut3" Obrzut <admin@maniacsvault.net>
22 //------------------------------------------------------------------------------
23 
24 #include "datapaths.h"
25 #include "gui/configuration/engineconfigpage.h"
26 #include "ini/ini.h"
27 #include "irc/entities/ircnetworkentity.h"
28 #include "log.h"
29 #include "pathfinder/pathfind.h"
30 #include "plugins/enginedefaults.h"
31 #include "plugins/engineplugin.h"
32 #include "serverapi/gameexefactory.h"
33 #include "serverapi/gamefile.h"
34 #include "serverapi/gamehost.h"
35 #include "serverapi/server.h"
36 #include "strings.hpp"
37 
38 #include <cstdarg>
39 #include <QPixmap>
40 #include <QRegularExpression>
41 
42 EnginePlugin::Data::Data()
43 {
44  // Init the defaults.
45  allowsConnectPassword = false;
46  allowsClientSlots = true;
47  allowsPlayerSlots = true;
48  allowsEmail = false;
49  allowsJoinPassword = false;
50  allowsMOTD = false;
51  allowsRConPassword = false;
52  allowsURL = false;
53  allowsUpnp = false;
54  allowsUpnpPort = false;
55  allowsLogging = false;
56  broadcast = nullptr;
57  clientOnly = false;
59  defaultServerPort = 10666;
60  demoExtensionAutomatic = true;
61  demoExtension = "lmp";
62  hasIwad = true;
63  hasMapList = true;
64  icon = nullptr;
65  inGameFileDownloads = false;
66  masterClient = nullptr;
67  pConfig = nullptr;
68  refreshThreshold = 2;
69  supportsRandomMapRotation = false;
70  valid = true;
71  version = 0;
72  aboutProvider.reset();
73 }
74 
76 
77 EnginePlugin::EnginePlugin()
78 {
79  d = new Data;
80 
81  d->gameExeFactory = QSharedPointer<GameExeFactory>(new GameExeFactory(this));
82  d->difficulty = QSharedPointer<DefaultDifficultyProvider>(new DefaultDifficultyProvider());
83 
84  // At the moment I can't think of how we would support any ABI other than
85  // the current, but I suppose we might as well keep track of it?
86  d->abiVersion = DOOMSEEKER_ABI_VERSION;
87 }
88 
89 EnginePlugin::~EnginePlugin()
90 {
91  delete d->icon;
92  delete d->pConfig;
93  delete d;
94 }
95 
97 {
98  return new EngineConfigPage(this, *d->pConfig, parent);
99 }
100 
102 {
103  return d->defaultMaster;
104 }
105 
106 QList<DMFlagsSection> EnginePlugin::dmFlags() const
107 {
108  return QList<DMFlagsSection>();
109 }
110 
111 GameExeFactory *EnginePlugin::gameExe()
112 {
113  return data()->gameExeFactory.data();
114 }
115 
117 {
118  return new GameHost(this);
119 }
120 
121 QList<GameMode> EnginePlugin::gameModes() const
122 {
123  return QList<GameMode>();
124 }
125 
126 QList<GameCVar> EnginePlugin::gameModifiers() const
127 {
128  return QList<GameCVar>();
129 }
130 
131 void EnginePlugin::init(const char *name, const char *const icon[], ...)
132 {
133  d->name = name;
134  d->icon = new QPixmap(icon);
135  d->scheme = QString(d->name).replace(' ', "");
136 
137  va_list va;
138  va_start(va, icon);
139 
140  int feature;
141  while ((feature = va_arg(va, int)) != EP_Done)
142  {
143  switch (feature)
144  {
145  default:
146  // Since we don't know if the feature has arguments we must abort.
147  gLog << QString("%1 plugin attempted to use unknown feature.").arg(name);
148  d->valid = false;
149  return;
150 
151  case EP_Author:
152  d->author = va_arg(va, const char *);
153  break;
154  case EP_Version:
155  d->version = va_arg(va, unsigned int);
156  break;
157  case EP_AboutProvider:
158  d->aboutProvider.reset(va_arg(va, TextProvider *));
159  break;
160 
162  d->allowsConnectPassword = true;
163  break;
164  case EP_AllowsEmail:
165  d->allowsEmail = true;
166  break;
167  case EP_AllowsURL:
168  d->allowsURL = true;
169  break;
171  d->allowsJoinPassword = true;
172  break;
173  case EP_AllowsLogging:
174  d->allowsLogging = true;
175  break;
177  d->allowsRConPassword = true;
178  break;
179  case EP_AllowsMOTD:
180  d->allowsMOTD = true;
181  break;
182  case EP_AllowsUpnp:
183  d->allowsUpnp = true;
184  break;
185  case EP_AllowsUpnpPort:
186  d->allowsUpnpPort = true;
187  break;
188  case EP_Broadcast:
189  d->broadcast = va_arg(va, Broadcast *);
190  break;
191  case EP_CanonicalName:
192  d->canonicalName = va_arg(va, const char *);
193  break;
194  case EP_ClientOnly:
195  d->clientOnly = true;
196  break;
197  case EP_DefaultMaster:
198  d->defaultMaster = va_arg(va, const char *);
199  break;
201  d->defaultServerPort = va_arg(va, unsigned int);
202  break;
203  case EP_DemoExtension:
204  d->demoExtensionAutomatic = va_arg(va, unsigned int);
205  d->demoExtension = va_arg(va, const char *);
206  break;
208  d->difficulty = QSharedPointer<GameCVarProvider>(va_arg(va, GameCVarProvider *));
209  break;
211  d->createDMFlagsPagesAutomatic = false;
212  break;
214  d->inGameFileDownloads = true;
215  break;
216  case EP_IRCChannel:
217  {
218  // Either create an entity or put the channel in an existing one.
219  IRCNetworkEntity entity;
220  entity.setDescription(va_arg(va, const char *));
221  entity.setAddress(va_arg(va, const char *));
222  entity.autojoinChannels() << va_arg(va, const char *);
223 
224  if (d->ircChannels.contains(entity))
225  {
226  IRCNetworkEntity &existingEntity = d->ircChannels[d->ircChannels.indexOf(entity)];
227  existingEntity.autojoinChannels() << entity.autojoinChannels()[0];
228  }
229  else
230  d->ircChannels << entity;
231  break;
232  }
233  case EP_MasterClient:
234  d->masterClient = va_arg(va, MasterClient *);
235  break;
236  case EP_NoClientSlots:
237  d->allowsClientSlots = false;
238  break;
239  case EP_NoPlayerSlots:
240  d->allowsPlayerSlots = false;
241  break;
242  case EP_NoIwad:
243  d->hasIwad = false;
244  break;
245  case EP_NoMapList:
246  d->hasMapList = false;
247  break;
249  d->supportsRandomMapRotation = true;
250  break;
251  case EP_URLScheme:
252  d->scheme = va_arg(va, const char *);
253  break;
254  case EP_RefreshThreshold:
255  d->refreshThreshold = va_arg(va, unsigned int);
256  break;
257  case EP_ClientExeName:
258  d->clientExeName = va_arg(va, const char *);
259  break;
260  case EP_ServerExeName:
261  d->serverExeName = va_arg(va, const char *);
262  break;
264  {
265  QString suffixes = va_arg(va, const char *);
266  d->gameFileSearchSuffixes = suffixes.split(";", Qt::SkipEmptyParts);
267  break;
268  }
269  }
270  }
271 
272  va_end(va);
273 }
274 
275 void EnginePlugin::masterHost(QString &host, unsigned short &port) const
276 {
277  QString str = d->pConfig->setting("Masterserver");
278  Strings::translateServerAddress(str, host, port, d->defaultMaster);
279 }
280 
282 {
283  if (!d->canonicalName.isEmpty())
284  {
285  return d->canonicalName;
286  }
287  else
288  {
289  // Derive.
290  QString name = data()->name;
291  name = name.toLower();
292  name = name.replace(QRegularExpression("\\s"), "_");
293  return name;
294  }
295 }
296 
297 ServerPtr EnginePlugin::server(const QHostAddress &address, unsigned short port) const
298 {
299  ServerPtr server = mkServer(address, port);
300  if (server != nullptr)
301  server->setSelf(server.toWeakRef());
302  return server;
303 }
304 
305 void EnginePlugin::setConfig(IniSection &ini)
306 {
307  d->pConfig = new IniSection(ini);
308 
309  ini.createSetting("Masterserver", data()->defaultMaster);
310  findGameFiles(ini);
311 
312  setupConfig(ini);
313 }
314 
316 {
317  Q_UNUSED(config);
318 }
319 
320 void EnginePlugin::setGameExeFactory(QSharedPointer<GameExeFactory> factory)
321 {
322  d->gameExeFactory = factory;
323 }
324 
325 void EnginePlugin::findGameFiles(IniSection &ini)
326 {
327  for (const GameFile &file : gameExe()->gameFiles().asQList())
328  {
329  if (!ini.hasSetting(file.configName()))
330  {
331  QString path = PathFind::findGameFile(collectKnownPaths(ini), file);
332  ini[file.configName()] = gDefaultDataPaths->portablizePath(path);
333  }
334  }
335 }
336 
337 QStringList EnginePlugin::collectKnownPaths(const IniSection &ini) const
338 {
339  QStringList paths;
340  for (const GameFile &file : data()->gameExeFactory->gameFiles().asQList())
341  {
342  QString path = ini.retrieveSetting(file.configName()).valueString();
343  if (!path.isEmpty())
344  {
345  paths << path;
346  }
347  }
348  return paths;
349 }
350 
352 {
353 }