Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local sockets #888

Merged
merged 7 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion client/client_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,8 @@ int client_main(int argc, char *argv[])
auto positional = parser.positionalArguments();
if (positional.size() == 1) {
url = QUrl(positional.constFirst());
if (!url.isValid() || url.scheme() != QStringLiteral("fc21")) {
// Supported schemes: fc21://, fc21+local://
if (!url.isValid() || (!url.scheme().startsWith("fc21"))) {
// Try with the default protocol
url = QUrl(QStringLiteral("fc21://") + positional.constFirst());
// Still no luck
Expand Down
153 changes: 90 additions & 63 deletions client/clinet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#endif

// Qt
#include <QLocalSocket>
#include <QString>
#include <QTcpSocket>
#include <QTimer>
Expand Down Expand Up @@ -99,71 +100,89 @@ static void client_conn_close_callback(struct connection *pconn)
}

namespace {
/**
Try to connect to a server:
- try to create a TCP socket to the given URL (default to
localhost:DEFAULT_SOCK_PORT).
- if successful:
- start monitoring the socket for packets from the server
- send a "login request" packet to the server
and - return 0
- if unable to create the connection, close the socket, put an error
message in ERRBUF and return the Unix error code (ie., errno, which
will be non-zero).
*/

/// The URL we are presently connecting to.
QUrl connect_to;

static int try_to_connect(const QUrl &url, char *errbuf, int errbufsize)
/**
* Called when there is an error on the socket.
*/
static void error_on_socket()
{
// Apply defaults
auto url_copy = url;
if (url_copy.host().isEmpty()) {
url_copy.setHost(QStringLiteral("localhost"));
}
if (url_copy.port() <= 0) {
url_copy.setPort(DEFAULT_SOCK_PORT);
if (client.conn.sock != nullptr) {
log_debug("%s", qUtf8Printable(client.conn.sock->errorString()));
real_output_window_append(client.conn.sock->errorString(), NULL);
}
client.conn.used = false;
}

/**
* Try to connect to a server:
* - try to create a TCP socket to the given URL
* - if successful:
* - start monitoring the socket for packets from the server
* - send a "login request" packet to the server
* and - return 0
* - if unable to create the connection, close the socket, put an error
* message in ERRBUF and return the Unix error code (ie., errno, which
* will be non-zero).
*/
static int try_to_connect(const QUrl &url, char *errbuf, int errbufsize)
{
connections_set_close_callback(client_conn_close_callback);

// connection in progress? wait.
if (client.conn.used) {
if (url_copy != connect_to) {
if (url != connect_to) {
(void) fc_strlcpy(
errbuf, _("Canceled previous connection, trying new connection."),
errbufsize);
client.conn.sock->abort();
connect_to = url_copy;
if (auto tcp = qobject_cast<QTcpSocket *>(client.conn.sock)) {
tcp->abort();
} else if (auto local =
qobject_cast<QLocalSocket *>(client.conn.sock)) {
local->abort();
}
connect_to = url;
} else {
(void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize);
return -1;
}
}

// Reset
if (client.conn.sock) {
client.conn.sock->disconnect(client.conn.sock);
client.conn.sock->deleteLater();
}
client.conn.used = true; // Now there will be a connection :)
client.conn.sock->disconnect(client.conn.sock);

// Connect
if (!client.conn.sock) {
client.conn.sock = new QTcpSocket;
if (url.scheme() == QStringLiteral("fc21")) {
QTcpSocket *sock = new QTcpSocket;
client.conn.sock = sock;
QObject::connect(
sock,
QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
&error_on_socket);
QObject::connect(sock, &QTcpSocket::connected,
[userName = url.userName()] {
make_connection(client.conn.sock, userName);
});
sock->connectToHost(url.host(), url.port());
} else if (url.scheme() == QStringLiteral("fc21+local")) {
QLocalSocket *sock = new QLocalSocket;
client.conn.sock = sock;
QObject::connect(
sock,
QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error),
&error_on_socket);
QObject::connect(sock, &QLocalSocket::connected,
[userName = url.userName()] {
make_connection(client.conn.sock, userName);
});
sock->connectToServer(url.path());
}
QObject::connect(
client.conn.sock,
QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
[] {
if (client.conn.sock != nullptr) {
log_debug("%s", qUtf8Printable(client.conn.sock->errorString()));
real_output_window_append(client.conn.sock->errorString(),
nullptr);
}
client.conn.used = false;
});
QObject::connect(client.conn.sock, &QTcpSocket::connected,
[userName = url.userName()] {
make_connection(client.conn.sock, userName);
});

client.conn.sock->connectToHost(url.host(), url.port());

return 0;
}
} // namespace
Expand Down Expand Up @@ -193,7 +212,7 @@ int connect_to_server(const QUrl &url, char *errbuf, int errbufsize)
/**
Called after a connection is completed (e.g., in try_to_connect).
*/
void make_connection(QTcpSocket *sock, const QString &username)
void make_connection(QIODevice *sock, const QString &username)
{
struct packet_server_join_req req;

Expand Down Expand Up @@ -261,7 +280,7 @@ void disconnect_from_server()
*/
static int read_from_connection(struct connection *pc, bool block)
{
QTcpSocket *socket = pc->sock;
auto socket = pc->sock;
bool have_data_for_server =
(pc->used && pc->send_buffer && 0 < pc->send_buffer->ndata);

Expand All @@ -277,7 +296,7 @@ static int read_from_connection(struct connection *pc, bool block)

if (block) {
// Wait (and block the main event loop) until we get some data
socket->waitForReadyRead();
socket->waitForReadyRead(-1);
}

// Consume everything
Expand All @@ -302,7 +321,7 @@ static int read_from_connection(struct connection *pc, bool block)
This function is called when the client received a new input from the
server.
*/
void input_from_server(QTcpSocket *sock)
void input_from_server(QIODevice *sock)
{
int nb;

Expand Down Expand Up @@ -360,20 +379,28 @@ void try_to_autoconnect(const QUrl &url)
client.conn.sock = new QTcpSocket;
}

QObject::connect(
client.conn.sock,
QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
[url] {
if (client.conn.sock != nullptr) {
output_window_printf(ftc_client,
_("Failed to autoconnect to %s: %d of %d "
"attempts, retrying..."),
qUtf8Printable(url.toDisplayString()), count,
MAX_AUTOCONNECT_ATTEMPTS);
QTimer::singleShot(AUTOCONNECT_INTERVAL,
[url]() { try_to_autoconnect(url); });
}
});

try_to_connect(url, errbuf, sizeof(errbuf));

auto error_handler = [url] {
if (client.conn.sock != nullptr) {
output_window_printf(ftc_client,
_("Failed to autoconnect to %s: %d of %d "
"attempts, retrying..."),
qUtf8Printable(url.toDisplayString()), count,
MAX_AUTOCONNECT_ATTEMPTS);
QTimer::singleShot(AUTOCONNECT_INTERVAL,
[url]() { try_to_autoconnect(url); });
}
};
if (auto tcp = qobject_cast<QTcpSocket *>(client.conn.sock)) {
QObject::connect(
tcp,
QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
error_handler);
} else if (auto local = qobject_cast<QLocalSocket *>(client.conn.sock)) {
QObject::connect(
local,
QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error),
error_handler);
}
}
6 changes: 3 additions & 3 deletions client/clinet.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
#pragma once

// Forward declarations
class QTcpSocket;
class QIODevice;
class QString;
class QUrl;

int connect_to_server(const QUrl &url, char *errbuf, int errbufsize);

void make_connection(QTcpSocket *sock, const QString &username);
void make_connection(QIODevice *sock, const QString &username);

void input_from_server(QTcpSocket *sock);
void input_from_server(QIODevice *sock);
void disconnect_from_server();

void try_to_autoconnect(const QUrl &url);
12 changes: 7 additions & 5 deletions client/connectdlg_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <QStandardPaths>
#include <QTcpServer>
#include <QUrl>
#include <QUuid>

#include <cstdio>
#include <cstring>
Expand Down Expand Up @@ -238,15 +239,17 @@ bool client_start_server(const QString &user_name)
return false;
}

// Generate a (random) unique name for the local socket
auto uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
lmoureaux marked this conversation as resolved.
Show resolved Hide resolved

ruleset = QString::fromUtf8(tileset_what_ruleset(tileset));

// Set up the command-line parameters.
port_buf = QString::number(internal_server_port);
savesdir = QStringLiteral("%1/saves").arg(storage);
scensdir = QStringLiteral("%1/scenarios").arg(storage);

arguments << QStringLiteral("-p") << port_buf << QStringLiteral("--bind")
<< QStringLiteral("localhost") << QStringLiteral("-q")
arguments << QStringLiteral("--local") << uuid << QStringLiteral("-q")
<< QStringLiteral("1") << QStringLiteral("-e")
<< QStringLiteral("--saves") << savesdir
<< QStringLiteral("--scenarios") << scensdir
Expand Down Expand Up @@ -297,10 +300,9 @@ bool client_start_server(const QString &user_name)

// Local server URL
auto url = QUrl();
url.setScheme(QStringLiteral("fc21"));
url.setScheme(QStringLiteral("fc21+local"));
url.setUserName(user_name);
url.setHost(QStringLiteral("localhost"));
url.setPort(internal_server_port);
url.setPath(uuid);

// a reasonable number of tries
while (connect_to_server(
Expand Down
4 changes: 2 additions & 2 deletions client/gui-qt/fc_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ enum client_pages fc_client::current_page() { return page; }
/**
Add notifier for server input
*/
void fc_client::add_server_source(QTcpSocket *sock)
void fc_client::add_server_source(QIODevice *sock)
{
connect(sock, &QIODevice::readyRead, this, &fc_client::server_input);

Expand All @@ -299,7 +299,7 @@ void fc_client::closeEvent(QCloseEvent *event)
*/
void fc_client::server_input()
{
if (auto *socket = dynamic_cast<QTcpSocket *>(sender())) {
if (auto *socket = dynamic_cast<QIODevice *>(sender())) {
input_from_server(socket);
}
}
Expand Down
2 changes: 1 addition & 1 deletion client/gui-qt/fc_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class fc_client : public QMainWindow {
~fc_client() override;
QWidget *pages[static_cast<int>(PAGE_GAME) + 2];
void fc_main(QApplication *);
void add_server_source(QTcpSocket *socket);
void add_server_source(QIODevice *socket);

enum client_pages current_page();

Expand Down
2 changes: 1 addition & 1 deletion client/gui-qt/gui_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ void qtg_sound_bell()
This function is called after the client succesfully has connected
to the server.
*/
void qtg_add_net_input(QTcpSocket *sock) { king()->add_server_source(sock); }
void qtg_add_net_input(QIODevice *sock) { king()->add_server_source(sock); }

/**
Stop waiting for any server network data. See add_net_input().
Expand Down
4 changes: 2 additions & 2 deletions client/gui-qt/page_network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,12 +438,12 @@ void page_network::slot_connect()

switch (connection_status) {
case LOGIN_TYPE:
client_url().setScheme(QStringLiteral("fc21"));
client_url().setUserName(ui.connect_login_edit->text());
client_url().setHost(ui.connect_host_edit->text());
client_url().setPort(ui.connect_port_edit->text().toInt());

if (connect_to_server(client_url(), errbuf, sizeof(errbuf)) != -1) {
} else {
if (connect_to_server(client_url(), errbuf, sizeof(errbuf)) < 0) {
king->set_status_bar(QString::fromUtf8(errbuf));
output_window_append(ftc_client, errbuf);
}
Expand Down
2 changes: 1 addition & 1 deletion client/gui-qt/qtg_cxxside.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void qtg_canvas_put_text(QPixmap *pcanvas, int canvas_x, int canvas_y,

void qtg_set_rulesets(int num_rulesets, QStringList rulesets);
void qtg_options_extra_init();
void qtg_add_net_input(QTcpSocket *sock);
void qtg_add_net_input(QIODevice *sock);
void qtg_remove_net_input();
void qtg_real_conn_list_dialog_update(void *unused);
void qtg_close_connection_dialog();
Expand Down
2 changes: 1 addition & 1 deletion client/gui_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ void options_extra_init(void) { funcs.options_extra_init(); }
/**
Call add_net_input callback
*/
void add_net_input(QTcpSocket *sock) { funcs.add_net_input(sock); }
void add_net_input(QIODevice *sock) { funcs.add_net_input(sock); }

/**
Call remove_net_input callback
Expand Down
2 changes: 1 addition & 1 deletion client/gui_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct gui_funcs {

void (*set_rulesets)(int num_rulesets, QStringList rulesets);
void (*options_extra_init)();
void (*add_net_input)(QTcpSocket *sock);
void (*add_net_input)(QIODevice *sock);
void (*remove_net_input)();
void (*real_conn_list_dialog_update)(void *unused);
void (*close_connection_dialog)();
Expand Down
4 changes: 2 additions & 2 deletions client/include/gui_main_g.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#pragma once

// Forward declarations
class QTcpSocket;
class QIODevice;

// utility
#include "support.h" // bool type
Expand All @@ -28,7 +28,7 @@ GUI_FUNC_PROTO(void, options_extra_init, void)

GUI_FUNC_PROTO(void, real_conn_list_dialog_update, void *)
GUI_FUNC_PROTO(void, sound_bell, void)
GUI_FUNC_PROTO(void, add_net_input, QTcpSocket *)
GUI_FUNC_PROTO(void, add_net_input, QIODevice *)
GUI_FUNC_PROTO(void, remove_net_input, void)

GUI_FUNC_PROTO(void, set_unit_icon, int idx, struct unit *punit)
Expand Down
Loading