Refactor websocket stuff from main and introduce app states
This commit is contained in:
parent
352fa9a178
commit
c72e550d8a
@ -80,6 +80,9 @@ set(CLIENT_ONLY_SOURCES
|
||||
"src/client/gl.hpp"
|
||||
"src/client/main.cpp"
|
||||
"src/client/utils.hpp"
|
||||
"src/client/wsclient.hpp"
|
||||
"src/client/wsclient_easywsclient.cpp"
|
||||
"src/client/wsclient_emscripten.cpp"
|
||||
"src/gameview/characterview.hpp"
|
||||
"src/gameview/characterview.cpp"
|
||||
"src/gameview/client_session.hpp"
|
||||
|
||||
@ -14,105 +14,15 @@ App::App() :
|
||||
std::cout << "Initializing App..." << std::endl;
|
||||
|
||||
ApplySettings();
|
||||
AddChatMessage("Test!");
|
||||
// AddChatMessage("Test!");
|
||||
}
|
||||
|
||||
void App::Frame()
|
||||
{
|
||||
delta_time_ = time_ - prev_time_;
|
||||
prev_time_ = time_;
|
||||
ws_.Poll();
|
||||
|
||||
if (delta_time_ < 0.0f)
|
||||
{
|
||||
delta_time_ = 0.0f; // Prevent negative delta time
|
||||
}
|
||||
else if (delta_time_ > 0.1f)
|
||||
{
|
||||
delta_time_ = 0.1f; // Cap delta time to avoid large jumps
|
||||
}
|
||||
|
||||
if (session_)
|
||||
{
|
||||
game::view::UpdateInfo updinfo;
|
||||
updinfo.time = time_;
|
||||
updinfo.delta_time = delta_time_;
|
||||
session_->Update(updinfo);
|
||||
}
|
||||
|
||||
gfx::DrawListParams params{};
|
||||
params.screen_width = viewport_size_.x;
|
||||
params.screen_height = viewport_size_.y;
|
||||
params.env.clear_color = glm::vec3(0.1f);
|
||||
|
||||
dlist_.Clear();
|
||||
gui_.Begin(viewport_size_);
|
||||
|
||||
// draw session
|
||||
if (session_)
|
||||
{
|
||||
session_->Draw(dlist_, params, gui_);
|
||||
}
|
||||
|
||||
// draw stats
|
||||
UpdateStats();
|
||||
DrawStats();
|
||||
|
||||
// draw chat
|
||||
UpdateChat();
|
||||
DrawChat();
|
||||
|
||||
// draw menu
|
||||
if (menu_)
|
||||
{
|
||||
auto menu_size = menu_->MeasureSize();
|
||||
menu_->Draw(gui_, (glm::vec2(viewport_size_) - menu_size) * 0.5f);
|
||||
}
|
||||
|
||||
gui_.Render();
|
||||
renderer_.DrawList(dlist_, params);
|
||||
|
||||
++stat_frames_;
|
||||
}
|
||||
|
||||
void App::Connected()
|
||||
{
|
||||
std::cout << "WS connected" << std::endl;
|
||||
AddChatMessagePrefix("WebSocket", "^7f7připojeno");
|
||||
|
||||
// init session
|
||||
session_ = std::make_unique<game::view::ClientSession>(*this);
|
||||
}
|
||||
|
||||
void App::ProcessMessage(net::InMessage& msg)
|
||||
{
|
||||
if (!session_)
|
||||
return;
|
||||
|
||||
size_t s = msg.End() - msg.Ptr();
|
||||
// AddChatMessage("recvd: ^f00;" + std::to_string(s));
|
||||
|
||||
// std::cout << "App::ProcessMessage: received message of size " << s << " bytes" << std::endl;
|
||||
|
||||
if (!session_->ProcessMessage(msg))
|
||||
{
|
||||
std::cerr << "FAILED to process message!" << std::endl;
|
||||
}
|
||||
|
||||
// record stats
|
||||
++stat_msgs_;
|
||||
stat_msglen_total_ += s;
|
||||
stat_msglen_min_ = std::min(stat_msglen_min_, s);
|
||||
stat_msglen_max_ = std::max(stat_msglen_max_, s);
|
||||
}
|
||||
|
||||
void App::Disconnected(const std::string& reason)
|
||||
{
|
||||
std::cout << "WS disconnected" << std::endl;
|
||||
AddChatMessagePrefix("WebSocket", "^f77spojení je píči");
|
||||
|
||||
|
||||
// close session
|
||||
session_.reset();
|
||||
Update();
|
||||
Draw();
|
||||
}
|
||||
|
||||
void App::Input(game::PlayerInputType in, bool pressed, bool repeated)
|
||||
@ -163,6 +73,59 @@ void App::AddChatMessagePrefix(const std::string& prefix, const std::string& tex
|
||||
|
||||
App::~App() {}
|
||||
|
||||
void App::Update()
|
||||
{
|
||||
delta_time_ = time_ - prev_time_;
|
||||
prev_time_ = time_;
|
||||
|
||||
if (delta_time_ < 0.0f)
|
||||
{
|
||||
delta_time_ = 0.0f; // Prevent negative delta time
|
||||
}
|
||||
else if (delta_time_ > 0.1f)
|
||||
{
|
||||
delta_time_ = 0.1f; // Cap delta time to avoid large jumps
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
UpdateSession();
|
||||
UpdateStats();
|
||||
UpdateChat();
|
||||
}
|
||||
|
||||
void App::Draw()
|
||||
{
|
||||
|
||||
gfx::DrawListParams params{};
|
||||
params.screen_width = viewport_size_.x;
|
||||
params.screen_height = viewport_size_.y;
|
||||
params.env.clear_color = glm::vec3(0.1f);
|
||||
|
||||
dlist_.Clear();
|
||||
gui_.Begin(viewport_size_);
|
||||
|
||||
// draw session
|
||||
if (session_)
|
||||
{
|
||||
session_->Draw(dlist_, params, gui_);
|
||||
}
|
||||
|
||||
DrawStats();
|
||||
DrawChat();
|
||||
|
||||
// draw menu
|
||||
if (menu_)
|
||||
{
|
||||
auto menu_size = menu_->MeasureSize();
|
||||
menu_->Draw(gui_, (glm::vec2(viewport_size_) - menu_size) * 0.5f);
|
||||
}
|
||||
|
||||
gui_.Render();
|
||||
renderer_.DrawList(dlist_, params);
|
||||
|
||||
++stat_frames_;
|
||||
}
|
||||
|
||||
void App::UpdateChat()
|
||||
{
|
||||
// remove expired or over the limit messages
|
||||
@ -253,6 +216,28 @@ void App::ApplySensitivity()
|
||||
#define COL_LABEL "^ccc"
|
||||
#define COL_VALUE "^5ff"
|
||||
|
||||
void App::UpdateSession()
|
||||
{
|
||||
if (!session_)
|
||||
return;
|
||||
|
||||
game::view::UpdateInfo updinfo;
|
||||
updinfo.time = time_;
|
||||
updinfo.delta_time = delta_time_;
|
||||
session_->Update(updinfo);
|
||||
|
||||
if (connected_)
|
||||
{
|
||||
auto msg = session_->GetMsg();
|
||||
if (!msg.empty())
|
||||
{
|
||||
ws_.Send(msg);
|
||||
}
|
||||
|
||||
session_->ResetMsg();
|
||||
}
|
||||
}
|
||||
|
||||
void App::UpdateStats()
|
||||
{
|
||||
if (time_ < stats_time_ + 1.0f)
|
||||
@ -291,3 +276,127 @@ void App::DrawStats()
|
||||
pos.y += 30.0f;
|
||||
gui_.DrawTextAligned(msglen_text_, pos, glm::vec2(-1.0f, 0.0f));
|
||||
}
|
||||
|
||||
void App::Connect()
|
||||
{
|
||||
ws_.SetOnConnect([this]{
|
||||
connected_ = true;
|
||||
connecting_ = false;
|
||||
});
|
||||
|
||||
ws_.SetOnMessage([this](std::span<const char> data) {
|
||||
ProcessWsMessage(data);
|
||||
});
|
||||
|
||||
ws_.SetOnDisconnect([this]{
|
||||
connected_ = false;
|
||||
connecting_ = false;
|
||||
});
|
||||
|
||||
connecting_ = ws_.Connect(url_);
|
||||
}
|
||||
|
||||
void App::ProcessWsMessage(std::span<const char> data)
|
||||
{
|
||||
if (!session_)
|
||||
return;
|
||||
|
||||
// record stats
|
||||
size_t s = data.size();
|
||||
++stat_msgs_;
|
||||
stat_msglen_total_ += s;
|
||||
stat_msglen_min_ = std::min(stat_msglen_min_, s);
|
||||
stat_msglen_max_ = std::max(stat_msglen_max_, s);
|
||||
|
||||
net::InMessage msg(data.data(), data.size());
|
||||
if (!session_->ProcessMessage(msg))
|
||||
{
|
||||
std::cerr << "FAILED to process message!" << std::endl;
|
||||
local_error_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void App::UpdateState()
|
||||
{
|
||||
auto new_state = CheckStateTransition();
|
||||
if (new_state == state_)
|
||||
return;
|
||||
|
||||
EnterState(new_state);
|
||||
}
|
||||
|
||||
void App::EnterState(AppState state)
|
||||
{
|
||||
state_ = state;
|
||||
state_time_ = time_;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case APP_STATE_INIT:
|
||||
break;
|
||||
|
||||
case APP_STATE_LOADING:
|
||||
break;
|
||||
|
||||
case APP_STATE_IDLE:
|
||||
break;
|
||||
|
||||
case APP_STATE_CONNECT:
|
||||
AddChatMessagePrefix("WebSocket", "připojování na " + url_);
|
||||
Connect();
|
||||
break;
|
||||
|
||||
case APP_STATE_CONNECTED:
|
||||
AddChatMessagePrefix("WebSocket", "^7f7připojeno");
|
||||
session_ = std::make_unique<game::view::ClientSession>(*this);
|
||||
break;
|
||||
|
||||
case APP_STATE_DISCONNECTED:
|
||||
AddChatMessagePrefix("WebSocket", "^f77spojení je píči");
|
||||
session_.reset();
|
||||
AddChatMessagePrefix("WebSocket", "další pokus za 10 s");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AppState App::CheckStateTransition()
|
||||
{
|
||||
switch (state_)
|
||||
{
|
||||
case APP_STATE_INIT:
|
||||
return APP_STATE_LOADING;
|
||||
|
||||
case APP_STATE_LOADING:
|
||||
return APP_STATE_IDLE;
|
||||
|
||||
case APP_STATE_IDLE:
|
||||
return APP_STATE_CONNECT;
|
||||
|
||||
case APP_STATE_CONNECT:
|
||||
if (connected_)
|
||||
return APP_STATE_CONNECTED;
|
||||
|
||||
if (!connecting_)
|
||||
return APP_STATE_DISCONNECTED;
|
||||
|
||||
return APP_STATE_CONNECT;
|
||||
|
||||
case APP_STATE_CONNECTED:
|
||||
if (!connected_)
|
||||
return APP_STATE_DISCONNECTED;
|
||||
|
||||
return APP_STATE_CONNECTED;
|
||||
|
||||
case APP_STATE_DISCONNECTED:
|
||||
if (GetCurrentStateDuration() >= 10.0f)
|
||||
return APP_STATE_CONNECT;
|
||||
|
||||
return APP_STATE_DISCONNECTED;
|
||||
|
||||
default:
|
||||
return state_;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "net/inmessage.hpp"
|
||||
#include "gui/menu.hpp"
|
||||
#include "gameview/client_session.hpp"
|
||||
#include "wsclient.hpp"
|
||||
|
||||
struct ChatMessage
|
||||
{
|
||||
@ -20,6 +21,16 @@ struct ChatMessage
|
||||
glm::vec4 color = glm::vec4(1.0f);
|
||||
};
|
||||
|
||||
enum AppState
|
||||
{
|
||||
APP_STATE_INIT,
|
||||
APP_STATE_LOADING,
|
||||
APP_STATE_IDLE,
|
||||
APP_STATE_CONNECT,
|
||||
APP_STATE_CONNECTED,
|
||||
APP_STATE_DISCONNECTED,
|
||||
};
|
||||
|
||||
class App
|
||||
{
|
||||
public:
|
||||
@ -27,13 +38,10 @@ public:
|
||||
|
||||
void Frame();
|
||||
|
||||
void Connected();
|
||||
void ProcessMessage(net::InMessage& msg);
|
||||
void Disconnected(const std::string& reason);
|
||||
|
||||
void SetTime(float time) { time_ = time; }
|
||||
void SetViewportSize(int width, int height) { viewport_size_ = {width, height}; }
|
||||
|
||||
void SetUrl(const std::string& url) { url_ = url; }
|
||||
void SetUserName(const std::string& username) { username_ = username; }
|
||||
const std::string& GetUserName() const { return username_; }
|
||||
|
||||
@ -43,8 +51,6 @@ public:
|
||||
const float& GetTime() const { return time_; }
|
||||
float GetDeltaTime() const { return delta_time_; }
|
||||
|
||||
game::view::ClientSession* GetSession() { return session_.get(); }
|
||||
|
||||
audio::Master& GetAudioMaster() { return audiomaster_; }
|
||||
|
||||
void AddChatMessage(const std::string& text);
|
||||
@ -53,6 +59,9 @@ public:
|
||||
~App();
|
||||
|
||||
private:
|
||||
void Update();
|
||||
void Draw();
|
||||
|
||||
void UpdateChat();
|
||||
void DrawChat();
|
||||
|
||||
@ -61,9 +70,18 @@ private:
|
||||
void ApplyVolume();
|
||||
void ApplySensitivity();
|
||||
|
||||
void UpdateSession();
|
||||
void UpdateStats();
|
||||
void DrawStats();
|
||||
|
||||
void Connect();
|
||||
void ProcessWsMessage(std::span<const char> data);
|
||||
|
||||
void UpdateState();
|
||||
void EnterState(AppState state);
|
||||
AppState CheckStateTransition();
|
||||
float GetCurrentStateDuration() const { return time_ - state_time_; }
|
||||
|
||||
private:
|
||||
float time_ = 0.0f;
|
||||
glm::ivec2 viewport_size_ = {800, 600};
|
||||
@ -74,16 +92,23 @@ private:
|
||||
gfx::Renderer renderer_;
|
||||
gfx::DrawList dlist_;
|
||||
gui::Context gui_;
|
||||
|
||||
audio::Master audiomaster_;
|
||||
|
||||
WsClient ws_;
|
||||
std::string url_;
|
||||
std::string username_;
|
||||
bool connecting_ = false;
|
||||
bool connected_ = false;
|
||||
bool local_error_ = false;
|
||||
|
||||
std::unique_ptr<game::view::ClientSession> session_;
|
||||
|
||||
std::deque<ChatMessage> chat_;
|
||||
|
||||
std::unique_ptr<gui::Menu> menu_;
|
||||
|
||||
AppState state_ = APP_STATE_INIT;
|
||||
float state_time_ = 0.0f;
|
||||
|
||||
// settings
|
||||
int volume_ = 50;
|
||||
int sens_ = 50;
|
||||
|
||||
@ -11,15 +11,11 @@
|
||||
#include <emscripten/html5_webgl.h>
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <emscripten/websocket.h>
|
||||
#else
|
||||
#include <easywsclient.hpp>
|
||||
#endif // EMSCRIPTEN
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#pragma comment(lib, "ws2_32")
|
||||
#pragma comment(lib, "winmm.lib")
|
||||
#include <WinSock2.h>
|
||||
#include <windows.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
@ -239,144 +235,6 @@ static void PollEvents()
|
||||
#endif
|
||||
|
||||
|
||||
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(const char* url)
|
||||
{
|
||||
if (!emscripten_websocket_is_supported())
|
||||
{
|
||||
std::cerr << "EMSCRIPTEN WS NOT SUPPORTED" << std::endl;
|
||||
return false;
|
||||
}
|
||||
EmscriptenWebSocketCreateAttributes ws_attrs = {
|
||||
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(const char* url)
|
||||
{
|
||||
|
||||
#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(url));
|
||||
|
||||
if (!s_ws)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void WSSend(std::span<const char> msg)
|
||||
{
|
||||
static std::vector<uint8_t> data;
|
||||
data.resize(msg.size_bytes());
|
||||
memcpy(data.data(), msg.data(), msg.size_bytes());
|
||||
s_ws->sendBinary(data);
|
||||
}
|
||||
|
||||
static void WSPoll()
|
||||
{
|
||||
s_ws->poll();
|
||||
|
||||
auto ws_state = s_ws->getReadyState();
|
||||
if (ws_state == WebSocket::OPEN && !s_ws_connected)
|
||||
{
|
||||
s_ws_connected = true;
|
||||
s_app->Connected();
|
||||
}
|
||||
else if (ws_state != WebSocket::OPEN && s_ws_connected)
|
||||
{
|
||||
s_ws_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 bool can_update = false;
|
||||
static Uint32 last_update = 0;
|
||||
|
||||
@ -387,7 +245,6 @@ static void Frame()
|
||||
s_app->SetTime(current_time / 1000.0f); // Set time in seconds
|
||||
|
||||
PollEvents();
|
||||
WSPoll();
|
||||
|
||||
int width, height;
|
||||
SDL_GetWindowSize(s_window, &width, &height);
|
||||
@ -395,21 +252,6 @@ static void Frame()
|
||||
|
||||
s_app->Frame();
|
||||
|
||||
auto session = s_app->GetSession();
|
||||
if (session)
|
||||
{
|
||||
if (s_ws_connected)
|
||||
{
|
||||
auto msg = session->GetMsg();
|
||||
if (!msg.empty())
|
||||
{
|
||||
WSSend(msg);
|
||||
}
|
||||
}
|
||||
|
||||
session->ResetMsg();
|
||||
}
|
||||
|
||||
SDL_GL_SwapWindow(s_window);
|
||||
}
|
||||
|
||||
@ -435,9 +277,6 @@ static void Main() {
|
||||
if (s_url.empty())
|
||||
s_url = WS_URL;
|
||||
|
||||
if (!WSInit(s_url.c_str()))
|
||||
return;
|
||||
|
||||
InitSDL();
|
||||
|
||||
try
|
||||
@ -454,7 +293,7 @@ static void Main() {
|
||||
|
||||
s_app = std::make_unique<App>();
|
||||
s_app->SetUserName(s_username);
|
||||
s_app->AddChatMessagePrefix("WebSocket", "připojování na " + s_url);
|
||||
s_app->SetUrl(s_url);
|
||||
|
||||
can_update = true;
|
||||
|
||||
@ -489,7 +328,6 @@ static void Main() {
|
||||
|
||||
ShutdownGL();
|
||||
ShutdownSDL();
|
||||
WSClose();
|
||||
|
||||
#endif // EMSCRIPTEN
|
||||
}
|
||||
|
||||
27
src/client/wsclient.hpp
Normal file
27
src/client/wsclient.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <functional>
|
||||
|
||||
using WsConnectCallback = std::function<void()>;
|
||||
using WsMessageCallback = std::function<void(std::span<const char> data)>;
|
||||
using WsDisconnectCallback = std::function<void()>;
|
||||
|
||||
class WsClient
|
||||
{
|
||||
public:
|
||||
WsClient();
|
||||
|
||||
bool Connect(const std::string& endpoint);
|
||||
void Send(std::span<const char> data);
|
||||
void Poll();
|
||||
void Disconnect();
|
||||
|
||||
void SetOnConnect(WsConnectCallback cb);
|
||||
void SetOnMessage(WsMessageCallback cb);
|
||||
void SetOnDisconnect(WsDisconnectCallback cb);
|
||||
|
||||
~WsClient();
|
||||
};
|
||||
|
||||
126
src/client/wsclient_easywsclient.cpp
Normal file
126
src/client/wsclient_easywsclient.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#ifndef EMSCRIPTEN
|
||||
|
||||
#include "wsclient.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <easywsclient.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#pragma comment(lib, "ws2_32")
|
||||
// #pragma comment(lib, "winmm.lib")
|
||||
#include <WinSock2.h>
|
||||
#include <windows.h>
|
||||
// #include <chrono>
|
||||
// #include <thread>
|
||||
#endif
|
||||
|
||||
using namespace easywsclient;
|
||||
|
||||
static std::unique_ptr<WebSocket> s_ws;
|
||||
static bool s_connected = false;
|
||||
|
||||
static WsConnectCallback s_on_connect;
|
||||
static WsMessageCallback s_on_message;
|
||||
static WsDisconnectCallback s_on_disconnect;
|
||||
|
||||
WsClient::WsClient()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
INT rc;
|
||||
WSADATA wsaData;
|
||||
|
||||
rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (rc)
|
||||
{
|
||||
throw std::runtime_error("WSA init failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool WsClient::Connect(const std::string& endpoint)
|
||||
{
|
||||
s_ws = std::unique_ptr<WebSocket>(WebSocket::from_url(endpoint));
|
||||
|
||||
if (!s_ws)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WsClient::Send(std::span<const char> data)
|
||||
{
|
||||
static std::vector<uint8_t> data_u8;
|
||||
data_u8.resize(data.size_bytes());
|
||||
memcpy(data_u8.data(), data.data(), data.size_bytes());
|
||||
s_ws->sendBinary(data_u8);
|
||||
}
|
||||
|
||||
void WsClient::Poll()
|
||||
{
|
||||
if (!s_ws)
|
||||
return;
|
||||
|
||||
s_ws->poll();
|
||||
|
||||
auto ws_state = s_ws->getReadyState();
|
||||
if (ws_state == WebSocket::OPEN && !s_connected)
|
||||
{
|
||||
s_connected = true;
|
||||
if (s_on_connect)
|
||||
s_on_connect();
|
||||
}
|
||||
else if (ws_state != WebSocket::OPEN && s_connected)
|
||||
{
|
||||
s_connected = false;
|
||||
if (s_on_disconnect)
|
||||
s_on_disconnect();
|
||||
}
|
||||
|
||||
s_ws->dispatchBinary([&](const std::vector<uint8_t>& data_u8) {
|
||||
if (s_on_message)
|
||||
{
|
||||
std::span<const char> data(reinterpret_cast<const char*>(data_u8.data()), data_u8.size());
|
||||
s_on_message(data);
|
||||
}
|
||||
});
|
||||
|
||||
if (!s_connected)
|
||||
{
|
||||
s_ws.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void WsClient::Disconnect()
|
||||
{
|
||||
s_ws->close();
|
||||
}
|
||||
|
||||
void WsClient::SetOnConnect(WsConnectCallback cb)
|
||||
{
|
||||
s_on_connect = std::move(cb);
|
||||
}
|
||||
|
||||
void WsClient::SetOnMessage(WsMessageCallback cb)
|
||||
{
|
||||
s_on_message = std::move(cb);
|
||||
}
|
||||
|
||||
void WsClient::SetOnDisconnect(WsDisconnectCallback cb)
|
||||
{
|
||||
s_on_disconnect = std::move(cb);
|
||||
}
|
||||
|
||||
WsClient::~WsClient()
|
||||
{
|
||||
s_ws.reset();
|
||||
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // EMSCRIPTEN
|
||||
130
src/client/wsclient_emscripten.cpp
Normal file
130
src/client/wsclient_emscripten.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
#ifdef EMSCRIPTEN
|
||||
|
||||
#include "wsclient.hpp"
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <emscripten/websocket.h>
|
||||
|
||||
static EMSCRIPTEN_WEBSOCKET_T s_ws = 0;
|
||||
static bool s_connected = false;
|
||||
|
||||
static WsConnectCallback s_on_connect;
|
||||
static WsMessageCallback s_on_message;
|
||||
static WsDisconnectCallback s_on_disconnect;
|
||||
|
||||
static EM_BOOL OnWSOpen(int type, const EmscriptenWebSocketOpenEvent *ev, void *ud)
|
||||
{
|
||||
if (!s_connected)
|
||||
{
|
||||
s_connected = true;
|
||||
if (s_on_connect)
|
||||
s_on_connect();
|
||||
}
|
||||
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
static EM_BOOL OnWSMessage(int type, const EmscriptenWebSocketMessageEvent *ev, void *ud)
|
||||
{
|
||||
if (ev->isText)
|
||||
return EM_TRUE;
|
||||
|
||||
if (s_on_message)
|
||||
{
|
||||
std::span<const char> data(reinterpret_cast<char*>(ev->data), ev->numBytes);
|
||||
s_on_message(data);
|
||||
}
|
||||
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
static EM_BOOL OnWSClose(int type, const EmscriptenWebSocketCloseEvent *ev, void *ud)
|
||||
{
|
||||
if (s_connected)
|
||||
{
|
||||
s_connected = false;
|
||||
if (s_on_disconnect)
|
||||
{
|
||||
s_on_disconnect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
static EM_BOOL OnWSError(int type, const EmscriptenWebSocketErrorEvent *ev, void *ud)
|
||||
{
|
||||
s_connected = false;
|
||||
if (s_on_disconnect)
|
||||
{
|
||||
s_on_disconnect();
|
||||
}
|
||||
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
WsClient::WsClient()
|
||||
{
|
||||
if (!emscripten_websocket_is_supported())
|
||||
{
|
||||
throw std::runtime_error("EMSCRIPTEN WS NOT SUPPORTED");
|
||||
}
|
||||
}
|
||||
|
||||
bool WsClient::Connect(const std::string& endpoint)
|
||||
{
|
||||
if (s_ws)
|
||||
{
|
||||
emscripten_websocket_delete(s_ws);
|
||||
s_ws = 0;
|
||||
}
|
||||
|
||||
EmscriptenWebSocketCreateAttributes ws_attrs = {
|
||||
endpoint.c_str(),
|
||||
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;
|
||||
}
|
||||
|
||||
void WsClient::Send(std::span<const char> data)
|
||||
{
|
||||
emscripten_websocket_send_binary(s_ws, (void*)data.data(), static_cast<uint32_t>(data.size()));
|
||||
}
|
||||
|
||||
void WsClient::Poll()
|
||||
{
|
||||
}
|
||||
|
||||
void WsClient::Disconnect()
|
||||
{
|
||||
}
|
||||
|
||||
void WsClient::SetOnConnect(WsConnectCallback cb)
|
||||
{
|
||||
s_on_connect = std::move(cb);
|
||||
}
|
||||
|
||||
void WsClient::SetOnMessage(WsMessageCallback cb)
|
||||
{
|
||||
s_on_message = std::move(cb);
|
||||
}
|
||||
|
||||
void WsClient::SetOnDisconnect(WsDisconnectCallback cb)
|
||||
{
|
||||
s_on_disconnect = std::move(cb);
|
||||
}
|
||||
|
||||
WsClient::~WsClient()
|
||||
{
|
||||
}
|
||||
|
||||
#endif // EMSCRIPTEN
|
||||
Loading…
x
Reference in New Issue
Block a user