Freeciv21
Develop your civilization from humble roots to a global empire
clinet.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 #include <fc_config.h>
15 
16 // Qt
17 #include <QLocalSocket>
18 #include <QString>
19 #include <QTcpSocket>
20 #include <QUrl>
21 
22 // utility
23 #include "capstr.h"
24 #include "fcintl.h"
25 #include "log.h"
26 
27 // generated
28 #include "fc_version.h"
29 
30 // common
31 #include "packets.h"
32 
33 // client
34 #include "attribute.h"
35 #include "chatline_common.h"
36 #include "client_main.h"
37 #include "clinet.h"
38 #include "connectdlg_common.h"
39 #include "dialogs_g.h" // popdown_races_dialog()
40 #include "governor.h"
41 #include "options.h"
42 #include "packhand.h"
43 #include "qtg_cxxside.h"
44 
45 // In autoconnect mode, try to connect to once a second
46 #define AUTOCONNECT_INTERVAL 500
47 
48 // In autoconnect mode, try to connect 100 times
49 #define MAX_AUTOCONNECT_ATTEMPTS 100
50 
55 static void close_socket_nomessage(struct connection *pc)
56 {
58  pc->playing = nullptr;
59 
61 
63 }
64 
69 static void client_conn_close_callback(struct connection *pconn)
70 {
71  QString reason;
72 
73  if (pconn->sock != nullptr) {
74  reason = pconn->sock->errorString();
75  } else {
76  reason = QString::fromUtf8(_("unknown reason"));
77  }
78 
80  // If we lost connection to the internal server - kill it.
81  client_kill_server(true);
82  qCritical("Lost connection to server: %s.", qUtf8Printable(reason));
83  output_window_printf(ftc_client, _("Lost connection to server (%s)!"),
84  qUtf8Printable(reason));
85 }
86 
90 static void error_on_socket()
91 {
92  if (client.conn.sock != nullptr) {
93  log_debug("%s", qUtf8Printable(client.conn.sock->errorString()));
95  qUtf8Printable(client.conn.sock->errorString()));
96  }
97  client.conn.used = false;
98 }
99 
111 static int try_to_connect(const QUrl &url, char *errbuf, int errbufsize)
112 {
114 
115  // connection in progress? wait.
116  if (client.conn.used) {
117  (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize);
118  return -1;
119  }
120 
121  // Reset
122  if (client.conn.sock) {
123  client.conn.sock->disconnect(client.conn.sock);
124  client.conn.sock->deleteLater();
125  }
126  client.conn.used = true; // Now there will be a connection :)
127 
128  // Connect
129  if (url.scheme() == QStringLiteral("fc21")) {
130  QTcpSocket *sock = new QTcpSocket;
131  client.conn.sock = sock;
132  QObject::connect(sock, &QAbstractSocket::errorOccurred,
133  &error_on_socket);
134  QObject::connect(sock, &QTcpSocket::disconnected,
135  [] { client.conn.used = false; });
136  sock->connectToHost(url.host(), url.port());
137 
138  if (!sock->waitForConnected(10000)) {
139  (void) fc_strlcpy(errbuf, _("Connection timed out."), errbufsize);
140  return -1;
141  }
142  make_connection(sock, url.userName());
143 
144  } else if (url.scheme() == QStringLiteral("fc21+local")) {
145  QLocalSocket *sock = new QLocalSocket;
146  client.conn.sock = sock;
147  QObject::connect(sock, &QLocalSocket::errorOccurred, &error_on_socket);
148  QObject::connect(
149  sock, &QLocalSocket::disconnected,
150  [userName = url.userName()] { client.conn.used = false; });
151  sock->connectToServer(url.path());
152 
153  if (!sock->waitForConnected(10000)) {
154  (void) fc_strlcpy(errbuf, _("Connection timed out."), errbufsize);
155  return -1;
156  }
157  make_connection(sock, url.userName());
158  }
159 
160  return 0;
161 }
162 
167 int connect_to_server(const QUrl &url, char *errbuf, int errbufsize)
168 {
169  if (errbufsize > 0 && errbuf != nullptr) {
170  errbuf[0] = '\0';
171  }
172 
173  if (errbuf && 0 != try_to_connect(url, errbuf, errbufsize)) {
174  return -1;
175  }
176 
178  sz_strlcpy(gui_options->default_server_host, qUtf8Printable(url.host()));
179  gui_options->default_server_port = url.port(DEFAULT_SOCK_PORT);
180  }
181 
182  return 0;
183 }
184 
188 void make_connection(QIODevice *sock, const QString &username)
189 {
190  struct packet_server_join_req req;
191 
193  client.conn.sock = sock;
194  client.conn.client.last_request_id_used = 0;
195  client.conn.client.last_processed_request_id_seen = 0;
196  client.conn.client.request_id_of_currently_handled_packet = 0;
199 
200  // call gui-dependent stuff in gui_main.c
202 
203  // now send join_request package
204 
205  req.major_version = MAJOR_VERSION;
206  req.minor_version = MINOR_VERSION;
207  req.patch_version = PATCH_VERSION;
208  sz_strlcpy(req.version_label, VERSION_LABEL);
209  sz_strlcpy(req.capability, our_capability);
210  sz_strlcpy(req.username, qUtf8Printable(username));
211 
212  send_packet_server_join_req(&client.conn, &req);
213 }
214 
220 {
221  const bool force = !client.conn.used;
222 
223  attribute_flush();
224 
226 
227  /* If it's internal server - kill him
228  * We assume that we are always connected to the internal server */
229  if (!force) {
230  client_kill_server(false);
231  }
233  if (force) {
234  client_kill_server(true);
235  }
236  output_window_append(ftc_client, _("Disconnected from server."));
237 
239  options_save(nullptr);
240  }
241 }
242 
254 static int read_from_connection(struct connection *pc, bool block)
255 {
256  auto socket = pc->sock;
257  bool have_data_for_server =
258  (pc->used && pc->send_buffer && 0 < pc->send_buffer->ndata);
259 
260  if (!socket->isOpen()) {
261  return -2;
262  }
263 
264  // By the way, if there's some data available for the server let's send
265  // it now
266  if (have_data_for_server) {
268  }
269 
270  if (block) {
271  // Wait (and block the main event loop) until we get some data
272  socket->waitForReadyRead(-1);
273  }
274 
275  // Consume everything
276  int ret = 0;
277  while (socket->bytesAvailable() > 0) {
278  int result = read_socket_data(socket, pc->buffer);
279  if (result == 0) {
280  // There is data in the socket but we can't read it, probably the
281  // connection buffer is full.
282  break;
283  } else if (result > 0) {
284  ret += result;
285  } else {
286  // Error
287  return result;
288  }
289  }
290  return ret;
291 }
292 
297 void input_from_server(QIODevice *sock)
298 {
299  int nb;
300 
301  fc_assert_ret(sock == client.conn.sock);
302 
303  nb = read_from_connection(&client.conn, false);
304  if (0 <= nb) {
305  governor::i()->freeze();
306  while (client.conn.used) {
307  enum packet_type type;
308  void *packet = get_packet_from_connection(&client.conn, &type);
309 
310  if (nullptr != packet) {
311  client_packet_input(packet, type);
312  ::operator delete(packet);
313 
314  if (type == PACKET_PROCESSING_FINISHED) {
315  if (client.conn.client.last_processed_request_id_seen
316  >= cities_results_request()) {
318  }
319  }
320  } else {
321  break;
322  }
323  }
324  if (client.conn.used) {
325  governor::i()->unfreeze();
326  }
327  } else if (-2 == nb) {
328  connection_close(&client.conn, _("server disconnected"));
329  } else {
330  connection_close(&client.conn, _("read error"));
331  }
332 }
333 
334 static bool autoconnecting = false;
339 double try_to_autoconnect(const QUrl &url)
340 {
341  char errbuf[512];
342  static int count = 0;
343 
344  // Don't repeat autoconnect if not autoconnecting or the user
345  // established a connection by himself.
347  return FC_INFINITY;
348  }
349 
350  count++;
351 
352  if (count >= MAX_AUTOCONNECT_ATTEMPTS) {
353  qFatal(_("Failed to contact server \"%s\" after %d attempts"),
354  qUtf8Printable(url.toDisplayString()), count);
355  exit(EXIT_FAILURE);
356  }
357 
358  if (try_to_connect(url, errbuf, sizeof(errbuf)) == 0) {
359  // Success! Don't call me again
360  autoconnecting = false;
361  return FC_INFINITY;
362  } else {
363  // All errors are fatal
364  qCritical(_("Error contacting server \"%s\":\n %s\n"),
365  qUtf8Printable(url.toDisplayString()), errbuf);
366  exit(EXIT_FAILURE);
367  }
368 }
369 
377 void start_autoconnecting_to_server(const QUrl &url)
378 {
380  ftc_client,
381  _("Auto-connecting to \"%s\" every %f second(s) for %d times"),
382  qUtf8Printable(url.toDisplayString()), 0.001 * AUTOCONNECT_INTERVAL,
384 
385  autoconnecting = true;
386 }
void attribute_flush()
Send current state to the server.
Definition: attribute.cpp:284
const char *const our_capability
Definition: capstr.cpp:28
void output_window_append(const struct ft_color color, const char *featured_text)
Add a line of text to the output ("chatline") window, like puts() would do it in the console.
void output_window_printf(const struct ft_color color, const char *format,...)
Add a line of text to the output ("chatline") window.
static governor * i()
Definition: governor.cpp:107
void unfreeze()
Definition: governor.h:24
void freeze()
Definition: governor.h:23
void client_packet_input(void *packet, int type)
Handle packet received from server.
struct civclient client
void stop_turn_change_wait()
Server is responsive again.
void set_client_state(enum client_states newstate)
Change client state.
@ C_S_DISCONNECTED
Definition: client_main.h:41
static bool autoconnecting
Definition: clinet.cpp:334
static void error_on_socket()
Called when there is an error on the socket.
Definition: clinet.cpp:90
static void client_conn_close_callback(struct connection *pconn)
Client connection close socket callback.
Definition: clinet.cpp:69
void make_connection(QIODevice *sock, const QString &username)
Called after a connection is completed (e.g., in try_to_connect).
Definition: clinet.cpp:188
static int read_from_connection(struct connection *pc, bool block)
A wrapper around read_socket_data() which also handles the case the socket becomes writeable and ther...
Definition: clinet.cpp:254
void input_from_server(QIODevice *sock)
This function is called when the client received a new input from the server.
Definition: clinet.cpp:297
#define AUTOCONNECT_INTERVAL
Definition: clinet.cpp:46
double try_to_autoconnect(const QUrl &url)
Make an attempt to autoconnect to the server.
Definition: clinet.cpp:339
#define MAX_AUTOCONNECT_ATTEMPTS
Definition: clinet.cpp:49
static void close_socket_nomessage(struct connection *pc)
Close socket and cleanup.
Definition: clinet.cpp:55
void start_autoconnecting_to_server(const QUrl &url)
Start trying to autoconnect to freeciv21-server.
Definition: clinet.cpp:377
static int try_to_connect(const QUrl &url, char *errbuf, int errbufsize)
Try to connect to a server:
Definition: clinet.cpp:111
int connect_to_server(const QUrl &url, char *errbuf, int errbufsize)
Connect to a freeciv21-server instance – or at least try to.
Definition: clinet.cpp:167
void disconnect_from_server()
Get rid of server connection.
Definition: clinet.cpp:219
void client_kill_server(bool force)
Kills the server if the client has started it.
void connections_set_close_callback(conn_close_fn_t func)
Register the close_callback.
Definition: connection.cpp:61
void flush_connection_send_buffer_all(struct connection *pc)
Flush'em.
Definition: connection.cpp:177
int read_socket_data(QIODevice *sock, struct socket_packet_buffer *buffer)
Read data from socket, and check if a packet is ready.
Definition: connection.cpp:110
void connection_common_init(struct connection *pconn)
Initialize common part of connection structure.
Definition: connection.cpp:555
void connection_close(struct connection *pconn, const QString &reason)
Call the conn_close_callback.
Definition: connection.cpp:69
void connection_common_close(struct connection *pconn)
Connection closing part common to server and client.
Definition: connection.cpp:574
void popdown_races_dialog(void)
Close the nation selection dialog.
Definition: dialogs.cpp:1011
#define _(String)
Definition: fcintl.h:50
const struct ft_color ftc_client
int cities_results_request()
Definition: governor.cpp:204
void cma_got_result(int citynr)
Definition: governor.cpp:206
void add_net_input(QIODevice *sock)
Wait for data on the given socket.
Definition: gui_main.cpp:147
#define fc_assert_ret(condition)
Definition: log.h:112
#define log_debug(message,...)
Definition: log.h:65
client_options * gui_options
Definition: options.cpp:74
void options_save(option_save_log_callback log_cb)
Save all options.
Definition: options.cpp:4496
#define get_packet_from_connection(pc, ptype)
Definition: packets.h:90
void notify_about_incoming_packet(struct connection *pc, int packet_type, int size)
Notify interested parties about incoming packet.
Definition: packhand.cpp:5187
void notify_about_outgoing_packet(struct connection *pc, int packet_type, int size, int request_id)
Notify interested parties about outgoing packet.
Definition: packhand.cpp:5197
#define FC_INFINITY
Definition: shared.h:32
struct connection conn
Definition: client_main.h:89
int default_server_port
Definition: options.h:51
bool use_prev_server
Definition: options.h:52
char default_server_host[512]
Definition: options.h:50
bool save_options_on_exit
Definition: options.h:64
bool established
Definition: connection.h:131
struct player * playing
Definition: connection.h:142
QIODevice * sock
Definition: connection.h:129
void(* incoming_packet_notify)(struct connection *pc, int packet_type, int size)
Definition: connection.h:234
void(* outgoing_packet_notify)(struct connection *pc, int packet_type, int size, int request_id)
Definition: connection.h:242
struct connection::@55::@60 client
struct socket_packet_buffer * send_buffer
Definition: connection.h:145
struct socket_packet_buffer * buffer
Definition: connection.h:144
unsigned long ndata
Definition: connection.h:108
size_t fc_strlcpy(char *dest, const char *src, size_t n)
fc_strlcpy() provides utf-8 version of (non-standard) function strlcpy() It is intended as more user-...
Definition: support.cpp:412
#define sz_strlcpy(dest, src)
Definition: support.h:140