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