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/gl.hpp"
|
||||||
"src/client/main.cpp"
|
"src/client/main.cpp"
|
||||||
"src/client/utils.hpp"
|
"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.hpp"
|
||||||
"src/gameview/characterview.cpp"
|
"src/gameview/characterview.cpp"
|
||||||
"src/gameview/client_session.hpp"
|
"src/gameview/client_session.hpp"
|
||||||
|
|||||||
@ -14,105 +14,15 @@ App::App() :
|
|||||||
std::cout << "Initializing App..." << std::endl;
|
std::cout << "Initializing App..." << std::endl;
|
||||||
|
|
||||||
ApplySettings();
|
ApplySettings();
|
||||||
AddChatMessage("Test!");
|
// AddChatMessage("Test!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::Frame()
|
void App::Frame()
|
||||||
{
|
{
|
||||||
delta_time_ = time_ - prev_time_;
|
ws_.Poll();
|
||||||
prev_time_ = time_;
|
|
||||||
|
|
||||||
if (delta_time_ < 0.0f)
|
Update();
|
||||||
{
|
Draw();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::Input(game::PlayerInputType in, bool pressed, bool repeated)
|
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() {}
|
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()
|
void App::UpdateChat()
|
||||||
{
|
{
|
||||||
// remove expired or over the limit messages
|
// remove expired or over the limit messages
|
||||||
@ -253,6 +216,28 @@ void App::ApplySensitivity()
|
|||||||
#define COL_LABEL "^ccc"
|
#define COL_LABEL "^ccc"
|
||||||
#define COL_VALUE "^5ff"
|
#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()
|
void App::UpdateStats()
|
||||||
{
|
{
|
||||||
if (time_ < stats_time_ + 1.0f)
|
if (time_ < stats_time_ + 1.0f)
|
||||||
@ -291,3 +276,127 @@ void App::DrawStats()
|
|||||||
pos.y += 30.0f;
|
pos.y += 30.0f;
|
||||||
gui_.DrawTextAligned(msglen_text_, pos, glm::vec2(-1.0f, 0.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 "net/inmessage.hpp"
|
||||||
#include "gui/menu.hpp"
|
#include "gui/menu.hpp"
|
||||||
#include "gameview/client_session.hpp"
|
#include "gameview/client_session.hpp"
|
||||||
|
#include "wsclient.hpp"
|
||||||
|
|
||||||
struct ChatMessage
|
struct ChatMessage
|
||||||
{
|
{
|
||||||
@ -20,6 +21,16 @@ struct ChatMessage
|
|||||||
glm::vec4 color = glm::vec4(1.0f);
|
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
|
class App
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -27,13 +38,10 @@ public:
|
|||||||
|
|
||||||
void Frame();
|
void Frame();
|
||||||
|
|
||||||
void Connected();
|
|
||||||
void ProcessMessage(net::InMessage& msg);
|
|
||||||
void Disconnected(const std::string& reason);
|
|
||||||
|
|
||||||
void SetTime(float time) { time_ = time; }
|
void SetTime(float time) { time_ = time; }
|
||||||
void SetViewportSize(int width, int height) { viewport_size_ = {width, height}; }
|
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; }
|
void SetUserName(const std::string& username) { username_ = username; }
|
||||||
const std::string& GetUserName() const { return username_; }
|
const std::string& GetUserName() const { return username_; }
|
||||||
|
|
||||||
@ -43,8 +51,6 @@ public:
|
|||||||
const float& GetTime() const { return time_; }
|
const float& GetTime() const { return time_; }
|
||||||
float GetDeltaTime() const { return delta_time_; }
|
float GetDeltaTime() const { return delta_time_; }
|
||||||
|
|
||||||
game::view::ClientSession* GetSession() { return session_.get(); }
|
|
||||||
|
|
||||||
audio::Master& GetAudioMaster() { return audiomaster_; }
|
audio::Master& GetAudioMaster() { return audiomaster_; }
|
||||||
|
|
||||||
void AddChatMessage(const std::string& text);
|
void AddChatMessage(const std::string& text);
|
||||||
@ -53,6 +59,9 @@ public:
|
|||||||
~App();
|
~App();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Update();
|
||||||
|
void Draw();
|
||||||
|
|
||||||
void UpdateChat();
|
void UpdateChat();
|
||||||
void DrawChat();
|
void DrawChat();
|
||||||
|
|
||||||
@ -61,9 +70,18 @@ private:
|
|||||||
void ApplyVolume();
|
void ApplyVolume();
|
||||||
void ApplySensitivity();
|
void ApplySensitivity();
|
||||||
|
|
||||||
|
void UpdateSession();
|
||||||
void UpdateStats();
|
void UpdateStats();
|
||||||
void DrawStats();
|
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:
|
private:
|
||||||
float time_ = 0.0f;
|
float time_ = 0.0f;
|
||||||
glm::ivec2 viewport_size_ = {800, 600};
|
glm::ivec2 viewport_size_ = {800, 600};
|
||||||
@ -74,16 +92,23 @@ private:
|
|||||||
gfx::Renderer renderer_;
|
gfx::Renderer renderer_;
|
||||||
gfx::DrawList dlist_;
|
gfx::DrawList dlist_;
|
||||||
gui::Context gui_;
|
gui::Context gui_;
|
||||||
|
|
||||||
audio::Master audiomaster_;
|
audio::Master audiomaster_;
|
||||||
|
|
||||||
|
WsClient ws_;
|
||||||
|
std::string url_;
|
||||||
std::string username_;
|
std::string username_;
|
||||||
|
bool connecting_ = false;
|
||||||
|
bool connected_ = false;
|
||||||
|
bool local_error_ = false;
|
||||||
|
|
||||||
std::unique_ptr<game::view::ClientSession> session_;
|
std::unique_ptr<game::view::ClientSession> session_;
|
||||||
|
|
||||||
std::deque<ChatMessage> chat_;
|
std::deque<ChatMessage> chat_;
|
||||||
|
|
||||||
std::unique_ptr<gui::Menu> menu_;
|
std::unique_ptr<gui::Menu> menu_;
|
||||||
|
|
||||||
|
AppState state_ = APP_STATE_INIT;
|
||||||
|
float state_time_ = 0.0f;
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
int volume_ = 50;
|
int volume_ = 50;
|
||||||
int sens_ = 50;
|
int sens_ = 50;
|
||||||
|
|||||||
@ -11,15 +11,11 @@
|
|||||||
#include <emscripten/html5_webgl.h>
|
#include <emscripten/html5_webgl.h>
|
||||||
#include <emscripten/emscripten.h>
|
#include <emscripten/emscripten.h>
|
||||||
#include <emscripten/websocket.h>
|
#include <emscripten/websocket.h>
|
||||||
#else
|
|
||||||
#include <easywsclient.hpp>
|
|
||||||
#endif // EMSCRIPTEN
|
#endif // EMSCRIPTEN
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#pragma comment(lib, "ws2_32")
|
|
||||||
#pragma comment(lib, "winmm.lib")
|
#pragma comment(lib, "winmm.lib")
|
||||||
#include <WinSock2.h>
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -239,144 +235,6 @@ static void PollEvents()
|
|||||||
#endif
|
#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 bool can_update = false;
|
||||||
static Uint32 last_update = 0;
|
static Uint32 last_update = 0;
|
||||||
|
|
||||||
@ -387,7 +245,6 @@ static void Frame()
|
|||||||
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);
|
||||||
@ -395,21 +252,6 @@ static void Frame()
|
|||||||
|
|
||||||
s_app->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);
|
SDL_GL_SwapWindow(s_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,9 +277,6 @@ static void Main() {
|
|||||||
if (s_url.empty())
|
if (s_url.empty())
|
||||||
s_url = WS_URL;
|
s_url = WS_URL;
|
||||||
|
|
||||||
if (!WSInit(s_url.c_str()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
InitSDL();
|
InitSDL();
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -454,7 +293,7 @@ static void Main() {
|
|||||||
|
|
||||||
s_app = std::make_unique<App>();
|
s_app = std::make_unique<App>();
|
||||||
s_app->SetUserName(s_username);
|
s_app->SetUserName(s_username);
|
||||||
s_app->AddChatMessagePrefix("WebSocket", "připojování na " + s_url);
|
s_app->SetUrl(s_url);
|
||||||
|
|
||||||
can_update = true;
|
can_update = true;
|
||||||
|
|
||||||
@ -489,7 +328,6 @@ static void Main() {
|
|||||||
|
|
||||||
ShutdownGL();
|
ShutdownGL();
|
||||||
ShutdownSDL();
|
ShutdownSDL();
|
||||||
WSClose();
|
|
||||||
|
|
||||||
#endif // EMSCRIPTEN
|
#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