From bc935312b3fb0ea52a09ec810d0c3d6b5f48128f Mon Sep 17 00:00:00 2001 From: tovjemam Date: Thu, 8 Jan 2026 22:34:54 +0100 Subject: [PATCH] Implement Emscripten Websockets --- CMakeLists.txt | 1 + src/client/main.cpp | 224 ++++++++++++++++++++++++++++++++------------ 2 files changed, 163 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e684896..c073de2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Emscripten) ) target_link_options(${MAIN_NAME} PRIVATE + "-lwebsocket.js" "-sUSE_SDL=2" "-sASYNCIFY" "-sUSE_WEBGL2=1" diff --git a/src/client/main.cpp b/src/client/main.cpp index f72ca01..6fb465d 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -6,6 +6,8 @@ #ifdef EMSCRIPTEN #include #include +#include +#include #else #include #endif // EMSCRIPTEN @@ -151,12 +153,153 @@ static void PollEvents() } } +#define WS_URL "ws://deadfish.cz:11200/ws" + +static bool s_ws_connected = false; + +#ifdef EMSCRIPTEN + +static EMSCRIPTEN_WEBSOCKET_T s_ws = 0; + +static EM_BOOL OnWSOpen(int type, const EmscriptenWebSocketOpenEvent *ev, void *ud) +{ + s_ws_connected = true; + s_app->Connected(); + return EM_TRUE; +} + +static EM_BOOL OnWSMessage(int type, const EmscriptenWebSocketMessageEvent *ev, void *ud) +{ + if (ev->isText) + return EM_TRUE; + + net::InMessage msg(reinterpret_cast(ev->data), ev->numBytes); + s_app->ProcessMessage(msg); + + return EM_TRUE; +} + +static EM_BOOL OnWSClose(int type, const EmscriptenWebSocketCloseEvent *ev, void *ud) +{ + s_ws_connected = false; + s_app->Disconnected(""); + return EM_TRUE; +} + +static EM_BOOL OnWSError(int type, const EmscriptenWebSocketErrorEvent *ev, void *ud) +{ + s_ws_connected = false; + s_app->Disconnected("error"); + return EM_TRUE; +} + +static bool WSInit() +{ + if (!emscripten_websocket_is_supported()) + { + std::cerr << "EMSCRIPTEN WS NOT SUPPORTED" << std::endl; + return false; + } + EmscriptenWebSocketCreateAttributes ws_attrs = { + WS_URL, + NULL, + EM_TRUE + }; + + s_ws = emscripten_websocket_new(&ws_attrs); + emscripten_websocket_set_onopen_callback(s_ws, NULL, OnWSOpen); + emscripten_websocket_set_onmessage_callback(s_ws, NULL, OnWSMessage); + emscripten_websocket_set_onclose_callback(s_ws, NULL, OnWSClose); + emscripten_websocket_set_onerror_callback(s_ws, NULL, OnWSError); + + return true; +} + +static void WSSend(std::span data) +{ + emscripten_websocket_send_binary(s_ws, (void*)data.data(), static_cast(data.size())); +} + +static void WSPoll() {} +static void WSClose() {} + +#else /* !EMSCRIPTEN */ + +using namespace easywsclient; + +static std::unique_ptr s_ws; + +static bool WSInit() +{ + +#ifdef _WIN32 + INT rc; + WSADATA wsaData; + + rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (rc) + { + printf("WSAStartup Failed.\n"); + return false; + } +#endif + + s_ws = std::unique_ptr(WebSocket::from_url(WS_URL)); + + if (!s_ws) + return false; + + return true; +} + +static void WSSend(std::span data) +{ + static std::vector data; + data.resize(msg.size_bytes()); + memcpy(data.data(), msg.data(), msg.size_bytes()); + ws->sendBinary(data); +} + +static void WSPoll() +{ + s_ws->poll(); + + auto ws_state = s_ws->getReadyState(); + if (ws_state == WebSocket::OPEN && !connected) + { + connected = true; + s_app->Connected(); + } + else if (ws_state != WebSocket::OPEN && connected) + { + connected = false; + s_app->Disconnected("WS closed"); + } + + s_ws->dispatchBinary([&](const std::vector& data) { + net::InMessage msg(reinterpret_cast(data.data()), data.size()); + s_app->ProcessMessage(msg); + }); +} + +static void WSClose() +{ + s_ws.reset(); + +#ifdef _WIN32 + WSACleanup(); +#endif +} + +#endif /* EMSCRIPTEN */ + static void Frame() { Uint32 current_time = SDL_GetTicks(); s_app->SetTime(current_time / 1000.0f); // Set time in seconds PollEvents(); + WSPoll(); int width, height; SDL_GetWindowSize(s_window, &width, &height); @@ -204,10 +347,25 @@ static void Frame() s_app->SetInput(input); s_app->Frame(); + + if (s_ws_connected) + { + auto msg = s_app->GetMsg(); + if (!msg.empty()) + { + WSSend(msg); + } + } + + s_app->ResetMsg(); + SDL_GL_SwapWindow(s_window); } static void Main() { + if (!WSInit()) + return; + InitSDL(); try @@ -228,76 +386,18 @@ static void Main() { emscripten_set_main_loop(Frame, 0, true); #else -#ifdef _WIN32 - INT rc; - WSADATA wsaData; - - rc = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (rc) - { - printf("WSAStartup Failed.\n"); - return; - } -#endif - SDL_GL_SetSwapInterval(0); + while (!s_quit) { - using namespace easywsclient; - - auto ws = std::unique_ptr(WebSocket::from_url("ws://127.0.0.1:11200/ws")); - bool connected = false; - - - std::vector data; - - while (ws && !s_quit) - { - ws->poll(); - - auto ws_state = ws->getReadyState(); - if (ws_state == WebSocket::OPEN && !connected) - { - connected = true; - s_app->Connected(); - } - else if (ws_state != WebSocket::OPEN && connected) - { - connected = false; - s_app->Disconnected("WS closed"); - } - - ws->dispatchBinary([&](const std::vector& data) { - net::InMessage msg(reinterpret_cast(data.data()), data.size()); - s_app->ProcessMessage(msg); - }); - - Frame(); - - if (connected) - { - auto msg = s_app->GetMsg(); - if (!msg.empty()) - { - data.resize(msg.size_bytes()); - memcpy(data.data(), msg.data(), msg.size_bytes()); - ws->sendBinary(data); - } - } - - s_app->ResetMsg(); - } - + Frame(); } - + s_app.reset(); ShutdownGL(); ShutdownSDL(); - -#ifdef _WIN32 - WSACleanup(); -#endif + WSClose(); #endif // EMSCRIPTEN }