summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
authorPatric Stout <truebrain@openttd.org>2020-12-05 21:57:47 +0100
committerPatric Stout <github@truebrain.nl>2020-12-15 15:46:39 +0100
commitd15dc9f40f5a20bff452547a2dcb15231f9f969d (patch)
tree7b8d88635c048d906cbb6358007fb26055e24410 /src/network
parent2da07f76154d841bcfe9aaff4833144550186deb (diff)
downloadopenttd-d15dc9f40f5a20bff452547a2dcb15231f9f969d.tar.xz
Add: support for emscripten (play-OpenTTD-in-the-browser)
Emscripten compiles to WASM, which can be loaded via HTML / JavaScript. This allows you to play OpenTTD inside a browser. Co-authored-by: milek7 <me@milek7.pl>
Diffstat (limited to 'src/network')
-rw-r--r--src/network/core/address.cpp10
-rw-r--r--src/network/core/os_abstraction.h24
-rw-r--r--src/network/network.cpp11
-rw-r--r--src/network/network_content.cpp15
-rw-r--r--src/network/network_gui.cpp17
5 files changed, 73 insertions, 4 deletions
diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp
index c2fecc7ff..1aaa0b5fb 100644
--- a/src/network/core/address.cpp
+++ b/src/network/core/address.cpp
@@ -299,7 +299,15 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
if (!SetNoDelay(sock)) DEBUG(net, 1, "[%s] setting TCP_NODELAY failed", type);
- if (connect(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
+ int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen);
+#ifdef __EMSCRIPTEN__
+ /* Emscripten is asynchronous, and as such a connect() is still in
+ * progress by the time the call returns. */
+ if (err != 0 && errno != EINPROGRESS)
+#else
+ if (err != 0)
+#endif
+ {
DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, strerror(errno));
closesocket(sock);
return INVALID_SOCKET;
diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h
index 01ab68b27..be8b8f919 100644
--- a/src/network/core/os_abstraction.h
+++ b/src/network/core/os_abstraction.h
@@ -83,6 +83,16 @@ typedef unsigned long in_addr_t;
# include <errno.h>
# include <sys/time.h>
# include <netdb.h>
+
+# if defined(__EMSCRIPTEN__)
+/* Emscripten doesn't support AI_ADDRCONFIG and errors out on it. */
+# undef AI_ADDRCONFIG
+# define AI_ADDRCONFIG 0
+/* Emscripten says it supports FD_SETSIZE fds, but it really only supports 64.
+ * https://github.com/emscripten-core/emscripten/issues/1711 */
+# undef FD_SETSIZE
+# define FD_SETSIZE 64
+# endif
#endif /* UNIX */
/* OS/2 stuff */
@@ -148,12 +158,16 @@ typedef unsigned long in_addr_t;
*/
static inline bool SetNonBlocking(SOCKET d)
{
-#ifdef _WIN32
- u_long nonblocking = 1;
+#ifdef __EMSCRIPTEN__
+ return true;
#else
+# ifdef _WIN32
+ u_long nonblocking = 1;
+# else
int nonblocking = 1;
-#endif
+# endif
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
+#endif
}
/**
@@ -163,10 +177,14 @@ static inline bool SetNonBlocking(SOCKET d)
*/
static inline bool SetNoDelay(SOCKET d)
{
+#ifdef __EMSCRIPTEN__
+ return true;
+#else
/* XXX should this be done at all? */
int b = 1;
/* The (const char*) cast is needed for windows */
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0;
+#endif
}
/* Make sure these structures have the size we expect them to be */
diff --git a/src/network/network.cpp b/src/network/network.cpp
index 0e3d08630..a100b6b95 100644
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -1154,3 +1154,14 @@ bool IsNetworkCompatibleVersion(const char *other)
const char *hash2 = ExtractNetworkRevisionHash(other);
return hash1 && hash2 && (strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0);
}
+
+#ifdef __EMSCRIPTEN__
+extern "C" {
+
+void CDECL em_openttd_add_server(const char *host, int port)
+{
+ NetworkUDPQueryServer(NetworkAddress(host, port), true);
+}
+
+}
+#endif
diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp
index 5e401d3e9..0140d3ef2 100644
--- a/src/network/network_content.cpp
+++ b/src/network/network_content.cpp
@@ -23,6 +23,10 @@
#include <zlib.h>
#endif
+#ifdef __EMSCRIPTEN__
+# include <emscripten.h>
+#endif
+
#include "../safeguards.h"
extern bool HasScenario(const ContentInfo *ci, bool md5sum);
@@ -289,6 +293,13 @@ void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uin
{
bytes = 0;
+#ifdef __EMSCRIPTEN__
+ /* Emscripten is loaded via an HTTPS connection. As such, it is very
+ * difficult to make HTTP connections. So always use the TCP method of
+ * downloading content. */
+ fallback = true;
+#endif
+
ContentIDList content;
for (const ContentInfo *ci : this->infos) {
if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
@@ -535,6 +546,10 @@ void ClientNetworkContentSocketHandler::AfterDownload()
unlink(GetFullFilename(this->curInfo, false));
}
+#ifdef __EMSCRIPTEN__
+ EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
+#endif
+
this->OnDownloadComplete(this->curInfo->id);
} else {
ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, WL_ERROR);
diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp
index c430c47e5..47bf8fb69 100644
--- a/src/network/network_gui.cpp
+++ b/src/network/network_gui.cpp
@@ -39,6 +39,9 @@
#include "../safeguards.h"
+#ifdef __EMSCRIPTEN__
+# include <emscripten.h>
+#endif
static void ShowNetworkStartServerWindow();
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
@@ -475,6 +478,14 @@ public:
this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
this->SetFocusedWidget(WID_NG_FILTER);
+ /* As the master-server doesn't support "websocket" servers yet, we
+ * let "os/emscripten/pre.js" hardcode a list of servers people can
+ * join. This means the serverlist is curated for now, but it is the
+ * best we can offer. */
+#ifdef __EMSCRIPTEN__
+ EM_ASM(if (window["openttd_server_list"]) openttd_server_list());
+#endif
+
this->last_joined = NetworkGameListAddItem(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
this->server = this->last_joined;
if (this->last_joined != nullptr) NetworkUDPQueryServer(this->last_joined->address);
@@ -615,6 +626,12 @@ public:
this->GetWidget<NWidgetStacked>(WID_NG_NEWGRF_SEL)->SetDisplayedPlane(sel == nullptr || !sel->online || sel->info.grfconfig == nullptr);
this->GetWidget<NWidgetStacked>(WID_NG_NEWGRF_MISSING_SEL)->SetDisplayedPlane(sel == nullptr || !sel->online || sel->info.grfconfig == nullptr || !sel->info.version_compatible || sel->info.compatible);
+#ifdef __EMSCRIPTEN__
+ this->SetWidgetDisabledState(WID_NG_FIND, true);
+ this->SetWidgetDisabledState(WID_NG_ADD, true);
+ this->SetWidgetDisabledState(WID_NG_START, true);
+#endif
+
this->DrawWidgets();
}