7#include "absl/strings/str_cat.h"
8#include "absl/strings/str_format.h"
12#define CPPHTTPLIB_OPENSSL_SUPPORT
23class WebSocketClient::Impl {
25 Impl() : connected_(false), should_stop_(false) {}
31 absl::Status
Connect(
const std::string& host,
int port) {
32 std::lock_guard<std::mutex> lock(mutex_);
35 return absl::AlreadyExistsError(
"Already connected");
43 std::string url = absl::StrFormat(
"ws://%s:%d", host, port);
46 client_ = std::make_unique<httplib::Client>(host, port);
47 client_->set_connection_timeout(5, 0);
48 client_->set_read_timeout(30, 0);
54 receive_thread_ = std::thread([
this]() { ReceiveLoop(); });
56 return absl::OkStatus();
58 }
catch (
const std::exception& e) {
59 return absl::UnavailableError(
60 absl::StrCat(
"Failed to connect: ", e.what()));
65 std::lock_guard<std::mutex> lock(mutex_);
67 if (!connected_)
return;
72 if (receive_thread_.joinable()) {
73 receive_thread_.join();
79 absl::Status
Send(
const std::string& message) {
80 std::lock_guard<std::mutex> lock(mutex_);
83 return absl::FailedPreconditionError(
"Not connected");
89 auto res = client_->Post(
"/message", message,
"application/json");
92 return absl::UnavailableError(
"Failed to send message");
95 if (res->status != 200) {
96 return absl::InternalError(
97 absl::StrFormat(
"Server error: %d", res->status));
100 return absl::OkStatus();
102 }
catch (
const std::exception& e) {
103 return absl::InternalError(absl::StrCat(
"Send failed: ", e.what()));
108 std::lock_guard<std::mutex> lock(mutex_);
109 message_callback_ = callback;
113 std::lock_guard<std::mutex> lock(mutex_);
114 error_callback_ = callback;
118 std::lock_guard<std::mutex> lock(mutex_);
124 while (!should_stop_) {
127 std::this_thread::sleep_for(std::chrono::milliseconds(100));
132 }
catch (
const std::exception& e) {
133 if (error_callback_) {
134 error_callback_(e.what());
140 mutable std::mutex mutex_;
141 std::unique_ptr<httplib::Client> client_;
142 std::thread receive_thread_;
149 std::function<void(
const std::string&)> message_callback_;
150 std::function<void(
const std::string&)> error_callback_;
158 absl::Status
Connect(
const std::string&,
int) {
159 return absl::UnimplementedError(
"WebSocket support requires JSON library");
162 absl::Status
Send(
const std::string&) {
163 return absl::UnimplementedError(
"WebSocket support requires JSON library");
177 : impl_(std::make_unique<
Impl>()),
186 auto status =
impl_->Connect(host, port);
204 const std::string& session_name,
205 const std::string& username,
206 const std::string& rom_hash,
211 return absl::FailedPreconditionError(
"Not connected to server");
214 nlohmann::json message = {
215 {
"type",
"host_session"},
217 {
"session_name", session_name},
218 {
"username", username},
219 {
"rom_hash", rom_hash},
220 {
"ai_enabled", ai_enabled}
224 auto status =
SendRaw(message);
233 session.
host = username;
240 return absl::UnimplementedError(
"JSON support required");
245 const std::string& session_code,
246 const std::string& username) {
250 return absl::FailedPreconditionError(
"Not connected to server");
253 nlohmann::json message = {
254 {
"type",
"join_session"},
256 {
"session_code", session_code},
257 {
"username", username}
261 auto status =
SendRaw(message);
273 return absl::UnimplementedError(
"JSON support required");
280 return absl::FailedPreconditionError(
"Not in a session");
283 nlohmann::json message = {
284 {
"type",
"leave_session"},
288 auto status =
SendRaw(message);
292 return absl::UnimplementedError(
"JSON support required");
297 const std::string& message,
298 const std::string& sender) {
301 nlohmann::json msg = {
302 {
"type",
"chat_message"},
304 {
"message", message},
311 return absl::UnimplementedError(
"JSON support required");
316 const std::string& diff_data,
317 const std::string& rom_hash,
318 const std::string& sender) {
321 nlohmann::json message = {
322 {
"type",
"rom_sync"},
324 {
"diff_data", diff_data},
325 {
"rom_hash", rom_hash},
332 return absl::UnimplementedError(
"JSON support required");
337 const nlohmann::json& proposal_data,
338 const std::string& sender) {
341 nlohmann::json message = {
342 {
"type",
"proposal_share"},
345 {
"proposal_data", proposal_data}
351 return absl::UnimplementedError(
"JSON support required");
356 const std::string& proposal_id,
358 const std::string& username) {
361 nlohmann::json message = {
362 {
"type",
"proposal_vote"},
364 {
"proposal_id", proposal_id},
365 {
"approved", approved},
366 {
"username", username}
372 return absl::UnimplementedError(
"JSON support required");
377 const std::string& proposal_id,
378 const std::string& status) {
381 nlohmann::json message = {
382 {
"type",
"proposal_update"},
384 {
"proposal_id", proposal_id},
391 return absl::UnimplementedError(
"JSON support required");
409 return absl::FailedPreconditionError(
"Not in a session");
419 auto json = nlohmann::json::parse(message);
420 std::string type = json[
"type"];
424 for (
auto& callback : it->second) {
425 callback(json[
"payload"]);
428 }
catch (
const std::exception& e) {
429 HandleError(absl::StrCat(
"Failed to parse message: ", e.what()));
452 std::string msg_str = message.dump();
453 return impl_->Send(msg_str);
454 }
catch (
const std::exception& e) {
455 return absl::InternalError(absl::StrCat(
"Failed to serialize: ", e.what()));
458 return absl::UnimplementedError(
"JSON support required");
void SetErrorCallback(std::function< void(const std::string &)>)
void SetMessageCallback(std::function< void(const std::string &)>)
absl::Status Send(const std::string &)
absl::Status Connect(const std::string &, int)
void SetState(ConnectionState state)
absl::Status Connect(const std::string &host, int port=8765)
absl::StatusOr< SessionInfo > HostSession(const std::string &session_name, const std::string &username, const std::string &rom_hash, bool ai_enabled=true)
absl::Status VoteOnProposal(const std::string &proposal_id, bool approved, const std::string &username)
std::function< void(ConnectionState)> StateCallback
absl::Status UpdateProposalStatus(const std::string &proposal_id, const std::string &status)
absl::Status ShareProposal(const nlohmann::json &proposal_data, const std::string &sender)
absl::Status LeaveSession()
void OnStateChange(StateCallback callback)
absl::StatusOr< SessionInfo > GetSessionInfo() const
std::function< void(const std::string &)> ErrorCallback
std::unique_ptr< Impl > impl_
void OnMessage(const std::string &type, MessageCallback callback)
absl::Status SendChatMessage(const std::string &message, const std::string &sender)
void OnError(ErrorCallback callback)
std::function< void(const nlohmann::json &)> MessageCallback
std::vector< StateCallback > state_callbacks_
std::vector< ErrorCallback > error_callbacks_
std::map< std::string, std::vector< MessageCallback > > message_callbacks_
absl::StatusOr< SessionInfo > JoinSession(const std::string &session_code, const std::string &username)
absl::Status SendRomSync(const std::string &diff_data, const std::string &rom_hash, const std::string &sender)
SessionInfo current_session_
void HandleMessage(const std::string &message)
absl::Status SendRaw(const nlohmann::json &message)
void HandleError(const std::string &error)
ConnectionState
WebSocket connection states.
Main namespace for the application.
Information about the current collaboration session.