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