connectionhandler.cpp
1 //------------------------------------------------------------------------------
2 // connectionhandler.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) 2012 Braden "Blzut3" Obrzut <admin@maniacsvault.net>
22 // "Zalewa" <zalewapl@gmail.com>
23 //------------------------------------------------------------------------------
24 #include "connectionhandler.h"
25 
26 #include "joincommandlinebuilder.h"
27 #include "log.h"
28 #include "strings.hpp"
29 #include "configuration/doomseekerconfig.h"
30 #include "gui/configuration/doomseekerconfigurationdialog.h"
31 #include "gui/mainwindow.h"
32 #include "plugins/engineplugin.h"
33 #include "plugins/pluginloader.h"
34 #include "refresher/canrefreshserver.h"
35 #include "refresher/refresher.h"
36 #include "serverapi/gameclientrunner.h"
37 #include "serverapi/message.h"
38 #include "serverapi/server.h"
39 #include "application.h"
40 #include "apprunner.h"
41 #include "gamedemo.h"
42 #include <QDesktopServices>
43 #include <QMessageBox>
44 #include <QTimer>
45 #include <QUrl>
46 
47 DClass<ConnectionHandler>
48 {
49  public:
55  bool forceRefresh;
56  ServerPtr server;
57  QWidget *parentWidget;
58 };
59 
60 DPointered(ConnectionHandler)
61 
62 ConnectionHandler::ConnectionHandler(ServerPtr server, QWidget *parentWidget)
63 : QObject(parentWidget)
64 {
65  d->forceRefresh = false;
66  d->server = server;
67  d->parentWidget = parentWidget;
68 }
69 
70 ConnectionHandler::~ConnectionHandler()
71 {
72 }
73 
74 void ConnectionHandler::checkResponse(const ServerPtr &server, int response)
75 {
76  server->disconnect(this);
77  if(response != Server::RESPONSE_GOOD)
78  {
79  switch(response)
80  {
82  QMessageBox::critical(d->parentWidget, tr("Doomseeker - join server"),
83  tr("Connection to server timed out."));
84  break;
85  default:
86  QMessageBox::critical(d->parentWidget, tr("Doomseeker - join server"),
87  tr("An error occured while trying to connect to server."));
88  break;
89  }
90  finish(response);
91  return;
92  }
93 
94  // Since we're potentially arriving from a deeply nested network recv,
95  // it will be best to give the call stack a breather and continue execution
96  // from the next iteration of the main loop.
97  // This fixes a crash reported as #3268.
98  QTimer::singleShot(0, this, SLOT(buildJoinCommandLine()));
99 }
100 
101 ConnectionHandler *ConnectionHandler::connectByUrl(const QUrl &url)
102 {
103  gLog << QString("Attempting to connect to server: %1").arg(url.toString());
104 
105  // Locate plugin by scheme
106  const EnginePlugin *handler = NULL;
107  // For compatibility with IDE's zds://.../<two character> scheme
108  bool zdsScheme = url.scheme().compare("zds", Qt::CaseInsensitive) == 0;
109  for(unsigned int i = 0;i < gPlugins->numPlugins();++i)
110  {
111  const EnginePlugin *plugin = gPlugins->plugin(i)->info();
112  if(plugin->data()->scheme.compare(url.scheme(), Qt::CaseInsensitive) == 0 ||
113  (zdsScheme && plugin->data()->scheme.left(2).compare(url.path().mid(1), Qt::CaseInsensitive) == 0))
114  {
115  handler = plugin;
116  break;
117  }
118  }
119  if(handler == NULL)
120  {
121  gLog << "Scheme not recognized starting normally.";
122  return NULL;
123  }
124 
125  unsigned short port = url.port(handler->data()->defaultServerPort);
126  QString address;
127  // We can get the port through QUrl so we'll just create a temporary variable here.
128  unsigned short tmp;
129  Strings::translateServerAddress(url.host(), address, tmp, QString("localhost:10666"));
130 
131  // Create the server object
132  ServerPtr server = handler->server(QHostAddress(address), port);
133  ConnectionHandler *connectionHandler = new ConnectionHandler(server, NULL);
134  connectionHandler->d->forceRefresh = true;
135 
136  return connectionHandler;
137 }
138 
139 void ConnectionHandler::finish(int response)
140 {
141  emit finished(response);
142 }
143 
144 void ConnectionHandler::buildJoinCommandLine()
145 {
146  JoinCommandLineBuilder *builder = new JoinCommandLineBuilder(d->server,
147  gConfig.doomseeker.bRecordDemo ? GameDemo::Managed : GameDemo::NoDemo,
148  d->parentWidget);
149  this->connect(builder, SIGNAL(commandLineBuildFinished()), SLOT(onCommandLineBuildFinished()));
150  builder->obtainJoinCommandLine();
151 }
152 
153 void ConnectionHandler::onCommandLineBuildFinished()
154 {
155  JoinCommandLineBuilder *builder = static_cast<JoinCommandLineBuilder*>(sender());
156  CommandLineInfo builtCli = builder->builtCommandLine();
157  if (builtCli.isValid())
158  {
159  runCommandLine(builtCli);
160  }
161  else
162  {
163  if (!builder->error().isEmpty())
164  {
165  QMessageBox::critical(d->parentWidget, tr("Doomseeker - join game"), builder->error());
166  }
167  if (builder->isConfigurationError())
168  {
169  DoomseekerConfigurationDialog::openConfiguration(gApp->mainWindow(), d->server->plugin());
170  }
171  }
172  builder->deleteLater();
173  finish(Server::RESPONSE_GOOD);
174 }
175 
176 void ConnectionHandler::runCommandLine(const CommandLineInfo &cli)
177 {
178  Message message = AppRunner::runExecutable(cli);
179  if (message.isError())
180  {
181  gLog << tr("Error while launching executable for server \"%1\", game \"%2\": %3")
182  .arg(d->server->name()).arg(d->server->engineName()).arg(message.contents());
183  QMessageBox::critical(d->parentWidget, tr("Doomseeker - launch executable"), message.contents());
184  }
185 }
186 
187 void ConnectionHandler::refreshToJoin()
188 {
189  // If the data we have is old we should refresh first to check if we can
190  // still properly join the server.
191  CanRefreshServer refreshCheck(d->server.data());
192  if (d->forceRefresh || (refreshCheck.shouldRefresh() && gConfig.doomseeker.bQueryBeforeLaunch))
193  {
194  this->connect(d->server.data(), SIGNAL(updated(ServerPtr, int)),
195  SLOT(checkResponse(ServerPtr, int)));
196  gRefresher->registerServer(d->server.data());
197  }
198  else
199  {
200  checkResponse(d->server, Server::RESPONSE_GOOD);
201  }
202 }
203 
204 void ConnectionHandler::run()
205 {
206  refreshToJoin();
207 }
208 
209 // -------------------------- URL Handler -------------------------------------
210 
211 PluginUrlHandler *PluginUrlHandler::instance = NULL;
212 
213 void PluginUrlHandler::registerAll()
214 {
215  for(unsigned int i = 0;i < gPlugins->numPlugins();++i)
216  registerScheme(gPlugins->plugin(i)->info()->data()->scheme);
217 
218  // IDE compatibility
219  registerScheme("zds");
220 }
221 
222 void PluginUrlHandler::registerScheme(const QString &scheme)
223 {
224  if(!instance)
225  instance = new PluginUrlHandler();
226 
227  QDesktopServices::setUrlHandler(scheme, instance, "handleUrl");
228 }
229 
230 void PluginUrlHandler::handleUrl(const QUrl &url)
231 {
232  if(QMessageBox::question(NULL, tr("Connect to server"),
233  tr("Do you want to connect to the server at %1?").arg(url.toString()),
234  QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::Yes)
235  {
236  ConnectionHandler::connectByUrl(url);
237  }
238 }
static void translateServerAddress(const QString &addressString, QString &hostname, unsigned short &port, const QString &defaultAddress)
Translates string in format "hostname:port" to atomic values.
Definition: strings.cpp:393
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
bool isError() const
True if type() is equal to or greater than CUSTOM_ERROR.
Definition: message.cpp:104
Generates command line for joining specified server.
virtual ServerPtr server(const QHostAddress &address, unsigned short port) const
Creates an instance of server object from this plugin.
void obtainJoinCommandLine()
Runs asynchronously and emits commandLineBuildFinished() when done.
Response was parsed properly and Server information is available.
Definition: server.h:113
Server didn&#39;t respond at all.
Definition: server.h:117
QString contents() const
Customized displayable contents of this Message.
Definition: message.cpp:87
bool isValid() const
It&#39;s valid when at least executable is set.
Definition: apprunner.cpp:31