From 8d62d95107c23434dd84502e9e3c3130f4f52423 Mon Sep 17 00:00:00 2001 From: Zoltan Zarkov Date: Sat, 5 Feb 2022 19:25:51 -0600 Subject: [PATCH 1/4] Cherry-pick socket patch in Qt5 for WebAssembly --- .github/workflows/build.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f1992e262c..197afe6f76 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -192,14 +192,12 @@ jobs: QT_VERSION: 5.15.2 steps: - uses: actions/checkout@v2 - with: - fetch-depth: 0 - name: Cache Qt build id: qtcache uses: actions/cache@v2 with: path: ~/qt - key: qt-wasm-${{ env.QT_VERSION }}-svg2 + key: qt-wasm-${{ env.QT_VERSION }}-svg-socket - name: Install build tools run: | sudo apt-get update @@ -244,6 +242,10 @@ jobs: run: | source emsdk/emsdk_env.sh cd qtbase + git config --global user.email "root@example.com" + git config --global user.name "root" + git fetch origin 76d12eea2252c5e537dff15b103bdc1f925cf760 + git cherry-pick 76d12eea2252c5e537dff15b103bdc1f925cf760 ./configure -xplatform wasm-emscripten -opensource -confirm-license -prefix ~/qt -nomake examples make install - uses: actions/checkout@v2 From ca5264cffc6c0671dba418ba1a257098342c98ec Mon Sep 17 00:00:00 2001 From: Zoltan Zarkov Date: Sat, 5 Feb 2022 19:27:07 -0600 Subject: [PATCH 2/4] Update build options for WebAssembly client --- client/CMakeLists.txt | 29 ++++++++++++++++++++++++++++- client/gui-qt/CMakeLists.txt | 3 +++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index ddd723cc27..b9286c9d96 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -105,7 +105,34 @@ if(FREECIV_ENABLE_CLIENT) add_subdirectory(gui-qt) add_executable(freeciv21-client ${GUI_TYPE} packhand.cpp) # packhand.c depends on gui-qt if(EMSCRIPTEN) - target_link_options(freeciv21-client PRIVATE -sTOTAL_MEMORY=52428800) + target_link_options(freeciv21-client PRIVATE + -sASSERTIONS=1 + -sWEBSOCKET_URL=wss:// + -sTOTAL_MEMORY=52428800 + -sALLOW_MEMORY_GROWTH=1 + "-sEXPORTED_RUNTIME_METHODS=['UTF16ToString','stringToUTF16','callMain','printErr']" + --bind + -sUSE_SDL_MIXER=2 + "--preload-file=${CMAKE_SOURCE_DIR}/data/flags@data/flags" + "--preload-file=${CMAKE_SOURCE_DIR}/data/helpdata.txt@data/helpdata.txt" + "--preload-file=${CMAKE_SOURCE_DIR}/data/misc@data/misc" + "--preload-file=${CMAKE_SOURCE_DIR}/data/buildings@data/buildings" + "--preload-file=${CMAKE_SOURCE_DIR}/data/wonders@data/wonders" + "--preload-file=${CMAKE_SOURCE_DIR}/data/hexemplio.tilespec@data/hexemplio.tilespec" + "--preload-file=${CMAKE_SOURCE_DIR}/data/hexemplio@data/hexemplio" + "--preload-file=${CMAKE_SOURCE_DIR}/data/amplio2.tilespec@data/amplio2.tilespec" + "--preload-file=${CMAKE_SOURCE_DIR}/data/amplio2@data/amplio2" + "--preload-file=${CMAKE_SOURCE_DIR}/data/themes/gui-qt/Classic@data/themes/gui-qt/Classic" + "--preload-file=${CMAKE_SOURCE_DIR}/data/themes/gui-qt/icons@data/themes/gui-qt/icons" + "--preload-file=${CMAKE_SOURCE_DIR}/data/stdmusic.musicspec@data/stdmusic.musicspec" + "--preload-file=${CMAKE_SOURCE_DIR}/data/stdmusic@data/stdmusic" + "--preload-file=${CMAKE_SOURCE_DIR}/data/stdsounds@data/stdsounds" + "--preload-file=${CMAKE_SOURCE_DIR}/data/stdsounds.soundspec@data/stdsounds.soundspec" + "--exclude-file=*CMakeLists*" + "--exclude-file=*.icns" + "--exclude-file=*.serv" + "--exclude-file=*.svg" + ) endif() target_link_libraries(freeciv21-client client) target_link_libraries(freeciv21-client gui-qt) diff --git a/client/gui-qt/CMakeLists.txt b/client/gui-qt/CMakeLists.txt index 6afac8ad50..b02e22de1f 100644 --- a/client/gui-qt/CMakeLists.txt +++ b/client/gui-qt/CMakeLists.txt @@ -70,3 +70,6 @@ if(APPLE) INCLUDE Qt5::QCocoaIntegrationPlugin Qt5::QSvgPlugin ) endif() +if(EMSCRIPTEN) + qt5_import_plugins(gui-qt INCLUDE Qt5::QWasmIntegrationPlugin) +endif() From d13d8668938d4b318f96ab9e2001a7fd85933883 Mon Sep 17 00:00:00 2001 From: Zoltan Zarkov Date: Sat, 5 Feb 2022 19:28:35 -0600 Subject: [PATCH 3/4] Use connect event handler instead of wait call --- client/client_main.cpp | 8 +-- client/clinet.cpp | 107 ++++++++++++++++++++--------------------- client/clinet.h | 3 +- 3 files changed, 55 insertions(+), 63 deletions(-) diff --git a/client/client_main.cpp b/client/client_main.cpp index effeec912c..45601e43a0 100644 --- a/client/client_main.cpp +++ b/client/client_main.cpp @@ -773,8 +773,7 @@ void set_client_state(enum client_states newstate) qFatal(_("There was an error while auto connecting; aborting.")); exit(EXIT_FAILURE); } else { - start_autoconnecting_to_server(url); - auto_connect = false; // Don't try this again. + try_to_autoconnect(url); } } @@ -1081,11 +1080,6 @@ double real_timer_callback() voteinfo_queue_check_removed(); - { - double autoconnect_time = try_to_autoconnect(url); - time_until_next_call = MIN(time_until_next_call, autoconnect_time); - } - if (C_S_RUNNING != client_state()) { return time_until_next_call; } diff --git a/client/clinet.cpp b/client/clinet.cpp index 09bdd6bef4..e10cb90ed4 100644 --- a/client/clinet.cpp +++ b/client/clinet.cpp @@ -18,6 +18,7 @@ // Qt #include #include +#include #include // utility @@ -55,10 +56,10 @@ #include "clinet.h" // In autoconnect mode, try to connect to once a second -#define AUTOCONNECT_INTERVAL 500 +const int AUTOCONNECT_INTERVAL = 500; // In autoconnect mode, try to connect 100 times -#define MAX_AUTOCONNECT_ATTEMPTS 100 +const int MAX_AUTOCONNECT_ATTEMPTS = 100; /** Close socket and cleanup. This one doesn't print a message, so should @@ -96,6 +97,7 @@ static void client_conn_close_callback(struct connection *pconn) qUtf8Printable(reason)); } +namespace { /** Try to connect to a server: - try to create a TCP socket to the given URL (default to @@ -108,6 +110,9 @@ static void client_conn_close_callback(struct connection *pconn) message in ERRBUF and return the Unix error code (ie., errno, which will be non-zero). */ + +QUrl connect_to; + static int try_to_connect(const QUrl &url, char *errbuf, int errbufsize) { // Apply defaults @@ -123,35 +128,44 @@ static int try_to_connect(const QUrl &url, char *errbuf, int errbufsize) // connection in progress? wait. if (client.conn.used) { - (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize); - return -1; + if (url_copy != connect_to) { + (void) fc_strlcpy( + errbuf, _("Canceled previous connection, trying new connection."), + errbufsize); + client.conn.sock->abort(); + connect_to = url_copy; + } else { + (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize); + return -1; + } } 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; - QObject::connect( - client.conn.sock, - QOverload::of(&QAbstractSocket::error), - [] { - if (client.conn.sock != nullptr) { - log_debug("%s", qUtf8Printable(client.conn.sock->errorString())); - real_output_window_append(client.conn.sock->errorString(), NULL, - -1); - } - client.conn.used = false; - }); } - client.conn.sock->connectToHost(url.host(), url.port()); - if (!client.conn.sock->waitForConnected(-1)) { - errbuf[0] = '\0'; - return -1; - } - make_connection(client.conn.sock, url.userName()); + QObject::connect( + client.conn.sock, + QOverload::of(&QAbstractSocket::error), + [] { + if (client.conn.sock != nullptr) { + log_debug("%s", qUtf8Printable(client.conn.sock->errorString())); + real_output_window_append(client.conn.sock->errorString(), NULL, + -1); + } + 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 /** Connect to a freeciv21-server instance -- or at least try to. On success, @@ -324,22 +338,15 @@ void input_from_server(QTcpSocket *sock) } } -static bool autoconnecting = false; /** Make an attempt to autoconnect to the server. It returns number of seconds it should be called again. */ -double try_to_autoconnect(const QUrl &url) +void try_to_autoconnect(const QUrl &url) { char errbuf[512]; static int count = 0; - // Don't repeat autoconnect if not autoconnecting or the user - // established a connection by himself. - if (!autoconnecting || client.conn.established) { - return FC_INFINITY; - } - count++; if (count >= MAX_AUTOCONNECT_ATTEMPTS) { @@ -348,32 +355,24 @@ double try_to_autoconnect(const QUrl &url) exit(EXIT_FAILURE); } - if (try_to_connect(url, errbuf, sizeof(errbuf)) == 0) { - // Success! Don't call me again - autoconnecting = false; - return FC_INFINITY; - } else { - // All errors are fatal - qCritical(_("Error contacting server \"%s\":\n %s\n"), - qUtf8Printable(url.toDisplayString()), errbuf); - exit(EXIT_FAILURE); + if (!client.conn.sock) { + client.conn.sock = new QTcpSocket; } -} -/** - Start trying to autoconnect to freeciv21-server. Calls - get_server_address(), then arranges for try_to_autoconnect(), which - calls try_to_connect(), to be called roughly every - AUTOCONNECT_INTERVAL milliseconds, until success, fatal error or - user intervention. - */ -void start_autoconnecting_to_server(const QUrl &url) -{ - output_window_printf( - ftc_client, - _("Auto-connecting to \"%s\" every %f second(s) for %d times"), - qUtf8Printable(url.toDisplayString()), 0.001 * AUTOCONNECT_INTERVAL, - MAX_AUTOCONNECT_ATTEMPTS); + QObject::connect( + client.conn.sock, + QOverload::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); }); + } + }); - autoconnecting = true; + try_to_connect(url, errbuf, sizeof(errbuf)); } diff --git a/client/clinet.h b/client/clinet.h index 67cb79b449..be71225eb9 100644 --- a/client/clinet.h +++ b/client/clinet.h @@ -22,5 +22,4 @@ void make_connection(QTcpSocket *sock, const QString &username); void input_from_server(QTcpSocket *sock); void disconnect_from_server(); -double try_to_autoconnect(const QUrl &url); -void start_autoconnecting_to_server(const QUrl &url); +void try_to_autoconnect(const QUrl &url); From dccb054debe5f59b6327f69e055e0df77554889d Mon Sep 17 00:00:00 2001 From: Zoltan Zarkov Date: Sat, 5 Feb 2022 19:30:03 -0600 Subject: [PATCH 4/4] Set full screen and disable server scan in WebAssembly client --- client/gui-qt/fc_client.cpp | 4 ++++ client/gui-qt/page_network.cpp | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/client/gui-qt/fc_client.cpp b/client/gui-qt/fc_client.cpp index 9632ddf5a8..da3b8f22a3 100644 --- a/client/gui-qt/fc_client.cpp +++ b/client/gui-qt/fc_client.cpp @@ -61,6 +61,10 @@ extern "C" void real_science_report_dialog_update(void *); */ fc_client::fc_client() : QMainWindow(), current_file(QLatin1String("")) { +#ifdef __EMSCRIPTEN__ + setWindowFlags(Qt::FramelessWindowHint); + setWindowState(Qt::WindowFullScreen); +#endif QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); status_bar_queue.clear(); for (int i = 0; i <= PAGE_GAME; i++) { diff --git a/client/gui-qt/page_network.cpp b/client/gui-qt/page_network.cpp index 9d2dd736f8..3ce80d3bbb 100644 --- a/client/gui-qt/page_network.cpp +++ b/client/gui-qt/page_network.cpp @@ -284,6 +284,7 @@ void server_scan_error(struct server_scan *scan, const char *message) */ void page_network::destroy_server_scans() { +#ifndef __EMSCRIPTEN__ if (meta_scan) { server_scan_finish(meta_scan); meta_scan = NULL; @@ -305,6 +306,7 @@ void page_network::destroy_server_scans() lan_scan_timer->disconnect(); FC_FREE(lan_scan_timer); } +#endif } /** @@ -312,6 +314,7 @@ void page_network::destroy_server_scans() */ void page_network::update_network_lists() { +#ifndef __EMSCRIPTEN__ destroy_server_scans(); lan_scan_timer = new QTimer(this); @@ -331,6 +334,7 @@ void page_network::update_network_lists() &page_network::slot_meta_scan); meta_scan_timer->start(800); } +#endif } /** @@ -338,6 +342,7 @@ void page_network::update_network_lists() */ bool page_network::check_server_scan(server_scan *scan_data) { +#ifndef __EMSCRIPTEN__ struct server_scan *scan = scan_data; enum server_scan_status stat; @@ -357,6 +362,9 @@ bool page_network::check_server_scan(server_scan *scan_data) } return !(stat == SCAN_STATUS_ERROR || stat == SCAN_STATUS_DONE); +#else + return true; +#endif } /** @@ -502,6 +510,7 @@ void page_network::slot_selection_changed(const QItemSelection &selected, ui.lan_widget->clearSelection(); } +#ifndef __EMSCRIPTEN__ srvrs = server_scan_get_list(meta_scan); if (srvrs) { pserver = server_list_get(srvrs, index.row()); @@ -535,4 +544,5 @@ void page_network::slot_selection_changed(const QItemSelection &selected, ui.info_widget->setItem(k, col, item); } } +#endif }