mirror of
https://github.com/whoahq/whoa.git
synced 2026-03-18 13:41:06 +03:00
Adds the platform layer for building whoa as a WebAssembly application: Working: - CMake configuration for WHOA_SYSTEM_WEB with pthreads and ASYNCIFY - Web entry point and HTML shell template - Event loop adapted for emscripten_set_main_loop callback model - WebSocket-based networking (WowConnection over JS WebSocket API) - Sound system stubs (audio not yet implemented) - FetchFS for async file loading from web server - Freetype fixes for WASM compatibility (type mismatches) - Input handling for web canvas Missing (in separate commits): - WebGPU graphics backend (CGxDeviceWebGPU) - WGSL shaders - API selection in Device.cpp
175 lines
4.6 KiB
C++
175 lines
4.6 KiB
C++
#include "net/connection/WowConnectionNet.hpp"
|
|
#include "net/connection/WowConnection.hpp"
|
|
#include "net/connection/web/WsState.hpp"
|
|
#include <storm/Atomic.hpp>
|
|
#include <emscripten.h>
|
|
|
|
// Periodic callback that polls all connections for WebSocket events.
|
|
// Replaces the native select()/thread model with an interval-based poll.
|
|
static void webNetworkPoll(void* userData) {
|
|
auto net = static_cast<WowConnectionNet*>(userData);
|
|
|
|
// Copy connection pointers to a local array so we can safely iterate
|
|
// even if Service() modifies the connection list
|
|
WowConnection* conns[64];
|
|
int32_t count = 0;
|
|
|
|
net->m_connectionsLock.Enter();
|
|
|
|
for (auto conn = net->m_connections.Head(); conn && count < 64; conn = net->m_connections.Link(conn)->Next()) {
|
|
auto wsState = static_cast<WsState*>(conn->m_event);
|
|
if (!wsState) {
|
|
continue;
|
|
}
|
|
|
|
uint32_t flags = 0;
|
|
|
|
switch (conn->m_connState) {
|
|
case WOWC_CONNECTING:
|
|
if (wsState->connectPending || wsState->errorPending) {
|
|
flags |= 0x1; // DoWrites -> CheckConnect
|
|
}
|
|
break;
|
|
|
|
case WOWC_CONNECTED:
|
|
if (wsState->recvBuf.size > 0) {
|
|
flags |= 0x2; // DoReads
|
|
}
|
|
if (wsState->closePending || wsState->errorPending) {
|
|
flags |= 0x8; // DoDisconnect
|
|
}
|
|
break;
|
|
|
|
case WOWC_DISCONNECTING:
|
|
flags |= 0x8; // DoDisconnect
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (flags) {
|
|
conn->AddRef();
|
|
conn->m_serviceFlags = flags;
|
|
conns[count++] = conn;
|
|
}
|
|
}
|
|
|
|
net->m_connectionsLock.Leave();
|
|
|
|
// Process connections outside the lock
|
|
for (int32_t i = 0; i < count; i++) {
|
|
auto conn = conns[i];
|
|
uint32_t flags = conn->m_serviceFlags;
|
|
conn->m_serviceFlags = 0;
|
|
|
|
net->Service(conn, flags);
|
|
|
|
conn->Release();
|
|
}
|
|
}
|
|
|
|
void WowConnectionNet::Add(WowConnection* connection) {
|
|
this->m_connectionsLock.Enter();
|
|
|
|
if (!this->m_connections.IsLinked(connection)) {
|
|
this->m_connections.LinkToTail(connection);
|
|
this->PlatformAdd(connection);
|
|
}
|
|
|
|
this->m_connectionsLock.Leave();
|
|
}
|
|
|
|
void WowConnectionNet::Delete(WowConnection* connection) {
|
|
this->m_connectionsLock.Enter();
|
|
|
|
if (connection->m_refCount == 0) {
|
|
delete connection;
|
|
}
|
|
|
|
this->m_connectionsLock.Leave();
|
|
}
|
|
|
|
void WowConnectionNet::PlatformAdd(WowConnection* connection) {
|
|
// No TCP_NODELAY or fd_set management needed on web
|
|
}
|
|
|
|
void WowConnectionNet::PlatformChangeState(WowConnection* connection, WOW_CONN_STATE state) {
|
|
// No notification needed - polling handles state changes
|
|
}
|
|
|
|
void WowConnectionNet::PlatformInit(bool useEngine) {
|
|
}
|
|
|
|
void WowConnectionNet::PlatformRemove(WowConnection* connection) {
|
|
}
|
|
|
|
void WowConnectionNet::PlatformRun() {
|
|
// Not used on web - polling is driven by emscripten_set_interval
|
|
}
|
|
|
|
void WowConnectionNet::PlatformWorkerReady() {
|
|
}
|
|
|
|
void WowConnectionNet::Remove(WowConnection* connection) {
|
|
this->m_connectionsLock.Enter();
|
|
|
|
if (this->m_connections.IsLinked(connection)) {
|
|
this->m_connections.UnlinkNode(connection);
|
|
}
|
|
|
|
this->PlatformRemove(connection);
|
|
|
|
this->m_connectionsLock.Leave();
|
|
}
|
|
|
|
void WowConnectionNet::Run() {
|
|
// Not used on web
|
|
}
|
|
|
|
void WowConnectionNet::RunWorker(int32_t id) {
|
|
// Not used on web - no worker threads
|
|
}
|
|
|
|
void WowConnectionNet::Service(WowConnection* connection, uint32_t serviceFlags) {
|
|
while (serviceFlags) {
|
|
if (serviceFlags & 0x1) {
|
|
connection->DoWrites();
|
|
}
|
|
|
|
if (serviceFlags & 0x2) {
|
|
connection->DoReads();
|
|
}
|
|
|
|
if (serviceFlags & 0x4) {
|
|
connection->DoExceptions();
|
|
}
|
|
|
|
if (serviceFlags & 0x8) {
|
|
connection->DoDisconnect();
|
|
}
|
|
|
|
this->m_connectionsLock.Enter();
|
|
|
|
serviceFlags = connection->m_serviceFlags;
|
|
connection->m_serviceFlags = 0;
|
|
|
|
this->m_connectionsLock.Leave();
|
|
}
|
|
}
|
|
|
|
void WowConnectionNet::SignalWorker(WowConnection* connection, uint32_t flags) {
|
|
// On web, service directly instead of dispatching to worker threads
|
|
connection->AddRef();
|
|
connection->m_serviceFlags = flags;
|
|
|
|
this->Service(connection, flags);
|
|
|
|
connection->Release();
|
|
}
|
|
|
|
void WowConnectionNet::Start() {
|
|
// Use a periodic interval instead of threads to poll connections
|
|
emscripten_set_interval(webNetworkPoll, 16.0, this);
|
|
}
|