connectionhandler.cpp
1 //------------------------------------------------------------------------------
2 // connectionhandler.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) 2012 Braden 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.h"
29 #include "configuration/doomseekerconfig.h"
30 #include "gui/configuration/doomseekerconfigurationdialog.h"
31 #include "plugins/engineplugin.h"
32 #include "plugins/pluginloader.h"
33 #include "refresher/canrefreshserver.h"
34 #include "refresher/refresher.h"
35 #include "serverapi/gameclientrunner.h"
36 #include "serverapi/message.h"
37 #include "serverapi/server.h"
38 #include "apprunner.h"
39 #include "gamedemo.h"
40 #include <QDesktopServices>
41 #include <QMessageBox>
42 #include <QUrl>
43 
44 DClass<ConnectionHandler>
45 {
46  public:
47  bool handleResponse;
48  ServerPtr server;
49  QWidget *parentWidget;
50 };
51 
52 DPointered(ConnectionHandler)
53 
54 ConnectionHandler::ConnectionHandler(ServerPtr server, QWidget *parentWidget,
55  bool handleResponse)
56 : QObject(parentWidget)
57 {
58  d->handleResponse = handleResponse;
59  d->server = server;
60  d->parentWidget = parentWidget;
61  connect(d->server.data(), SIGNAL(updated(ServerPtr, int)),
62  this, SLOT(checkResponse(ServerPtr, int)));
63 }
64 
65 ConnectionHandler::~ConnectionHandler()
66 {
67 }
68 
69 void ConnectionHandler::checkResponse(const ServerPtr &server, int response)
70 {
71  if(response != Server::RESPONSE_GOOD)
72  {
73  if(d->handleResponse)
74  {
75  switch(response)
76  {
78  QMessageBox::critical(d->parentWidget, tr("Doomseeker - join server"), tr("Connection to server timed out."));
79  break;
80  default:
81  QMessageBox::critical(d->parentWidget, tr("Doomseeker - join server"), tr("An error occured while trying to connect to server."));
82  break;
83  }
84  }
85 
86  finish(response);
87  return;
88  }
89 
90  run();
91 }
92 
93 ConnectionHandler *ConnectionHandler::connectByUrl(const QUrl &url)
94 {
95  gLog << QString("Attempting to connect to server: %1").arg(url.toString());
96 
97  // Locate plugin by scheme
98  const EnginePlugin *handler = NULL;
99  // For compatibility with IDE's zds://.../<two character> scheme
100  bool zdsScheme = url.scheme().compare("zds", Qt::CaseInsensitive) == 0;
101  for(unsigned int i = 0;i < gPlugins->numPlugins();++i)
102  {
103  const EnginePlugin *plugin = gPlugins->plugin(i)->info();
104  if(plugin->data()->scheme.compare(url.scheme(), Qt::CaseInsensitive) == 0 ||
105  (zdsScheme && plugin->data()->scheme.left(2).compare(url.path().mid(1), Qt::CaseInsensitive) == 0))
106  {
107  handler = plugin;
108  break;
109  }
110  }
111  if(handler == NULL)
112  {
113  gLog << "Scheme not recognized starting normally.";
114  return NULL;
115  }
116 
117  unsigned short port = url.port(handler->data()->defaultServerPort);
118  QString address;
119  // We can get the port through QUrl so we'll just create a temporary variable here.
120  unsigned short tmp;
121  Strings::translateServerAddress(url.host(), address, tmp, QString("localhost:10666"));
122 
123  // Create the server object
124  ServerPtr server = handler->server(QHostAddress(address), port);
125  ConnectionHandler *connectionHandler = new ConnectionHandler(server, NULL, true);
126  gRefresher->registerServer(server.data());
127 
128  return connectionHandler;
129 }
130 
131 void ConnectionHandler::finish(int response)
132 {
133  d->server->disconnect(this);
134  emit finished(response);
135 }
136 
137 void ConnectionHandler::buildJoinCommandLine()
138 {
139  JoinCommandLineBuilder *builder = new JoinCommandLineBuilder(d->server,
140  gConfig.doomseeker.bRecordDemo ? GameDemo::Managed : GameDemo::NoDemo,
141  d->parentWidget);
142  this->connect(builder, SIGNAL(commandLineBuildFinished()), SLOT(onCommandLineBuildFinished()));
143  builder->obtainJoinCommandLine();
144 }
145 
146 void ConnectionHandler::onCommandLineBuildFinished()
147 {
148  JoinCommandLineBuilder *builder = static_cast<JoinCommandLineBuilder*>(sender());
149  CommandLineInfo builtCli = builder->builtCommandLine();
150  if (builtCli.isValid())
151  {
152  runCommandLine(builtCli);
153  }
154  else
155  {
156  if (!builder->error().isEmpty())
157  {
158  QMessageBox::critical(d->parentWidget, tr("Doomseeker - join game"), builder->error());
159  }
160  if (builder->isConfigurationError())
161  {
162  DoomseekerConfigurationDialog::openConfiguration(d->server->plugin());
163  }
164  }
165  builder->deleteLater();
166  finish(Server::RESPONSE_GOOD);
167 }
168 
169 void ConnectionHandler::runCommandLine(const CommandLineInfo &cli)
170 {
171  Message message = AppRunner::runExecutable(cli);
172  if (message.isError())
173  {
174  gLog << tr("Error while launching executable for server \"%1\", game \"%2\": %3")
175  .arg(d->server->name()).arg(d->server->engineName()).arg(message.contents());
176  QMessageBox::critical(d->parentWidget, tr("Doomseeker - launch executable"), message.contents());
177  }
178 }
179 
180 void ConnectionHandler::refreshToJoin()
181 {
182  // If the data we have is old we should refresh first to check if we can
183  // still properly join the server.
184  CanRefreshServer refreshCheck(d->server.data());
185  if(refreshCheck.shouldRefresh() && gConfig.doomseeker.bQueryBeforeLaunch)
186  {
187  gRefresher->registerServer(d->server.data());
188  }
189  else
190  {
191  checkResponse(d->server, Server::RESPONSE_GOOD);
192  }
193 }
194 
195 void ConnectionHandler::run()
196 {
197  buildJoinCommandLine();
198 }
199 
200 // -------------------------- URL Handler -------------------------------------
201 
202 PluginUrlHandler *PluginUrlHandler::instance = NULL;
203 
204 void PluginUrlHandler::registerAll()
205 {
206  for(unsigned int i = 0;i < gPlugins->numPlugins();++i)
207  registerScheme(gPlugins->plugin(i)->info()->data()->scheme);
208 
209  // IDE compatibility
210  registerScheme("zds");
211 }
212 
213 void PluginUrlHandler::registerScheme(const QString &scheme)
214 {
215  if(!instance)
216  instance = new PluginUrlHandler();
217 
218  QDesktopServices::setUrlHandler(scheme, instance, "handleUrl");
219 }
220 
221 void PluginUrlHandler::handleUrl(const QUrl &url)
222 {
223  if(QMessageBox::question(NULL, tr("Connect to server"),
224  tr("Do you want to connect to the server at %1?").arg(url.toString()),
225  QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::Yes)
226  {
227  ConnectionHandler::connectByUrl(url);
228  }
229 }
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:404
Structure holding parameters for application launch.
Definition: apprunner.h:37
Message object used to pass messages throughout the Doomseeker's system.
Definition: message.h:62
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'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's valid when at least executable is set.
Definition: apprunner.cpp:31