Implement Emscripten Websockets

This commit is contained in:
tovjemam 2026-01-08 22:34:54 +01:00
parent 09637d4db4
commit bc935312b3
2 changed files with 163 additions and 62 deletions

View File

@ -146,6 +146,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Emscripten)
) )
target_link_options(${MAIN_NAME} PRIVATE target_link_options(${MAIN_NAME} PRIVATE
"-lwebsocket.js"
"-sUSE_SDL=2" "-sUSE_SDL=2"
"-sASYNCIFY" "-sASYNCIFY"
"-sUSE_WEBGL2=1" "-sUSE_WEBGL2=1"

View File

@ -6,6 +6,8 @@
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
#include <emscripten.h> #include <emscripten.h>
#include <emscripten/html5_webgl.h> #include <emscripten/html5_webgl.h>
#include <emscripten/emscripten.h>
#include <emscripten/websocket.h>
#else #else
#include <easywsclient.hpp> #include <easywsclient.hpp>
#endif // EMSCRIPTEN #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<char*>(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<const char> data)
{
emscripten_websocket_send_binary(s_ws, (void*)data.data(), static_cast<uint32_t>(data.size()));
}
static void WSPoll() {}
static void WSClose() {}
#else /* !EMSCRIPTEN */
using namespace easywsclient;
static std::unique_ptr<WebSocket> 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>(WebSocket::from_url(WS_URL));
if (!s_ws)
return false;
return true;
}
static void WSSend(std::span<const char> data)
{
static std::vector<uint8_t> 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<uint8_t>& data) {
net::InMessage msg(reinterpret_cast<const char*>(data.data()), data.size());
s_app->ProcessMessage(msg);
});
}
static void WSClose()
{
s_ws.reset();
#ifdef _WIN32
WSACleanup();
#endif
}
#endif /* EMSCRIPTEN */
static void Frame() static void Frame()
{ {
Uint32 current_time = SDL_GetTicks(); Uint32 current_time = SDL_GetTicks();
s_app->SetTime(current_time / 1000.0f); // Set time in seconds s_app->SetTime(current_time / 1000.0f); // Set time in seconds
PollEvents(); PollEvents();
WSPoll();
int width, height; int width, height;
SDL_GetWindowSize(s_window, &width, &height); SDL_GetWindowSize(s_window, &width, &height);
@ -204,10 +347,25 @@ static void Frame()
s_app->SetInput(input); s_app->SetInput(input);
s_app->Frame(); 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); SDL_GL_SwapWindow(s_window);
} }
static void Main() { static void Main() {
if (!WSInit())
return;
InitSDL(); InitSDL();
try try
@ -228,76 +386,18 @@ static void Main() {
emscripten_set_main_loop(Frame, 0, true); emscripten_set_main_loop(Frame, 0, true);
#else #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); SDL_GL_SetSwapInterval(0);
while (!s_quit)
{ {
using namespace easywsclient; Frame();
auto ws = std::unique_ptr<WebSocket>(WebSocket::from_url("ws://127.0.0.1:11200/ws"));
bool connected = false;
std::vector<uint8_t> 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<uint8_t>& data) {
net::InMessage msg(reinterpret_cast<const char*>(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();
}
} }
s_app.reset(); s_app.reset();
ShutdownGL(); ShutdownGL();
ShutdownSDL(); ShutdownSDL();
WSClose();
#ifdef _WIN32
WSACleanup();
#endif
#endif // EMSCRIPTEN #endif // EMSCRIPTEN
} }