yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
z3ed_network_client.cc
Go to the documentation of this file.
2
3#include <chrono>
4#include <condition_variable>
5#include <mutex>
6#include <thread>
7
8#include "absl/strings/str_cat.h"
9#include "absl/strings/str_format.h"
10#include "absl/time/clock.h"
11#include "absl/time/time.h"
12
13#ifdef YAZE_WITH_JSON
14#include "nlohmann/json.hpp"
15#if !defined(_WIN32) && !defined(YAZE_IOS)
16#define CPPHTTPLIB_OPENSSL_SUPPORT
17#endif
18#include "httplib.h"
19#endif
20
21// Undefine Windows macros that conflict with our method names
22// Must be outside the #ifdef so it applies to all code below
23#ifdef _WIN32
24#ifdef SendMessage
25#undef SendMessage
26#endif
27#endif
28
29namespace yaze {
30namespace cli {
31namespace net {
32
33#ifdef YAZE_WITH_JSON
34
35// Implementation using httplib for cross-platform WebSocket support
36class Z3edNetworkClient::Impl {
37 public:
38 Impl() : connected_(false), in_session_(false) {}
39
40 ~Impl() { Disconnect(); }
41
42 absl::Status Connect(const std::string& host, int port) {
43 std::lock_guard<std::mutex> lock(mutex_);
44
45 if (connected_) {
46 return absl::AlreadyExistsError("Already connected");
47 }
48
49 host_ = host;
50 port_ = port;
51
52 try {
53 // Create HTTP client for WebSocket fallback
54 client_ = std::make_unique<httplib::Client>(host, port);
55 client_->set_connection_timeout(5, 0);
56 client_->set_read_timeout(30, 0);
57
58 // Test connection
59 auto res = client_->Get("/health");
60 if (!res || res->status != 200) {
61 return absl::UnavailableError("Server not responding");
62 }
63
64 connected_ = true;
65 return absl::OkStatus();
66
67 } catch (const std::exception& e) {
68 return absl::UnavailableError(
69 absl::StrCat("Connection failed: ", e.what()));
70 }
71 }
72
73 void Disconnect() {
74 std::lock_guard<std::mutex> lock(mutex_);
75 connected_ = false;
76 in_session_ = false;
77 client_.reset();
78 }
79
80 absl::Status JoinSession(const std::string& session_code,
81 const std::string& username) {
82 std::lock_guard<std::mutex> lock(mutex_);
83
84 if (!connected_) {
85 return absl::FailedPreconditionError("Not connected");
86 }
87
88 try {
89 nlohmann::json message = {
90 {"type", "join_session"},
91 {"payload",
92 {{"session_code", session_code}, {"username", username}}}};
93
94 auto res = client_->Post("/message", message.dump(), "application/json");
95
96 if (!res || res->status != 200) {
97 return absl::InternalError("Failed to join session");
98 }
99
100 in_session_ = true;
101 session_code_ = session_code;
102 username_ = username;
103
104 return absl::OkStatus();
105
106 } catch (const std::exception& e) {
107 return absl::InternalError(absl::StrCat("Join failed: ", e.what()));
108 }
109 }
110
111 absl::Status SubmitProposal(const std::string& description,
112 const std::string& proposal_json,
113 const std::string& username) {
114 std::lock_guard<std::mutex> lock(mutex_);
115
116 if (!connected_ || !in_session_) {
117 return absl::FailedPreconditionError("Not in a session");
118 }
119
120 try {
121 nlohmann::json proposal_data = nlohmann::json::parse(proposal_json);
122 proposal_data["description"] = description;
123
124 nlohmann::json message = {
125 {"type", "proposal_share"},
126 {"payload",
127 {{"sender", username}, {"proposal_data", proposal_data}}}};
128
129 auto res = client_->Post("/message", message.dump(), "application/json");
130
131 if (!res || res->status != 200) {
132 return absl::InternalError("Failed to submit proposal");
133 }
134
135 // Extract proposal ID from response if available
136 if (!res->body.empty()) {
137 try {
138 auto response_json = nlohmann::json::parse(res->body);
139 if (response_json.contains("proposal_id")) {
140 last_proposal_id_ = response_json["proposal_id"];
141 }
142 } catch (...) {
143 // Response parsing failed, continue
144 }
145 }
146
147 return absl::OkStatus();
148
149 } catch (const std::exception& e) {
150 return absl::InternalError(
151 absl::StrCat("Proposal submission failed: ", e.what()));
152 }
153 }
154
155 absl::StatusOr<std::string> GetProposalStatus(
156 const std::string& proposal_id) {
157 std::lock_guard<std::mutex> lock(mutex_);
158
159 if (!connected_) {
160 return absl::FailedPreconditionError("Not connected");
161 }
162
163 try {
164 // Query server for proposal status
165 auto res = client_->Get(
166 absl::StrFormat("/proposal/%s/status", proposal_id).c_str());
167
168 if (!res || res->status != 200) {
169 return absl::NotFoundError("Proposal not found");
170 }
171
172 auto response = nlohmann::json::parse(res->body);
173 return response["status"].get<std::string>();
174
175 } catch (const std::exception& e) {
176 return absl::InternalError(
177 absl::StrCat("Status check failed: ", e.what()));
178 }
179 }
180
181 absl::StatusOr<bool> WaitForApproval(const std::string& proposal_id,
182 int timeout_seconds) {
183 auto deadline = absl::Now() + absl::Seconds(timeout_seconds);
184
185 while (absl::Now() < deadline) {
186 auto status_result = GetProposalStatus(proposal_id);
187
188 if (!status_result.ok()) {
189 return status_result.status();
190 }
191
192 std::string status = *status_result;
193
194 if (status == "approved" || status == "applied") {
195 return true;
196 } else if (status == "rejected") {
197 return false;
198 }
199
200 // Poll every second
201 absl::SleepFor(absl::Seconds(1));
202 }
203
204 return absl::DeadlineExceededError("Approval timeout");
205 }
206
207 absl::Status SendMessage(const std::string& message,
208 const std::string& sender) {
209 std::lock_guard<std::mutex> lock(mutex_);
210
211 if (!connected_ || !in_session_) {
212 return absl::FailedPreconditionError("Not in a session");
213 }
214
215 try {
216 nlohmann::json msg = {
217 {"type", "chat_message"},
218 {"payload", {{"message", message}, {"sender", sender}}}};
219
220 auto res = client_->Post("/message", msg.dump(), "application/json");
221
222 if (!res || res->status != 200) {
223 return absl::InternalError("Failed to send message");
224 }
225
226 return absl::OkStatus();
227
228 } catch (const std::exception& e) {
229 return absl::InternalError(absl::StrCat("Send failed: ", e.what()));
230 }
231 }
232
233 absl::StatusOr<std::string> QueryAI(const std::string& query,
234 const std::string& username) {
235 std::lock_guard<std::mutex> lock(mutex_);
236
237 if (!connected_ || !in_session_) {
238 return absl::FailedPreconditionError("Not in a session");
239 }
240
241 try {
242 nlohmann::json message = {
243 {"type", "ai_query"},
244 {"payload", {{"query", query}, {"username", username}}}};
245
246 auto res = client_->Post("/message", message.dump(), "application/json");
247
248 if (!res || res->status != 200) {
249 return absl::InternalError("AI query failed");
250 }
251
252 // Wait for response (in a real implementation, this would use callbacks)
253 // For now, return placeholder
254 return std::string("AI agent endpoint not configured");
255
256 } catch (const std::exception& e) {
257 return absl::InternalError(absl::StrCat("AI query failed: ", e.what()));
258 }
259 }
260
261 bool IsConnected() const {
262 std::lock_guard<std::mutex> lock(mutex_);
263 return connected_;
264 }
265
266 std::string GetLastProposalId() const {
267 std::lock_guard<std::mutex> lock(mutex_);
268 return last_proposal_id_;
269 }
270
271 private:
272 mutable std::mutex mutex_;
273 std::unique_ptr<httplib::Client> client_;
274
275 std::string host_;
276 int port_;
277 bool connected_;
278 bool in_session_;
279
280 std::string session_code_;
281 std::string username_;
282 std::string last_proposal_id_;
283};
284
285#else
286
287// Stub implementation when JSON is not available
289 public:
290 absl::Status Connect(const std::string&, int) {
291 return absl::UnimplementedError("Network support requires JSON library");
292 }
293 void Disconnect() {}
294 absl::Status JoinSession(const std::string&, const std::string&) {
295 return absl::UnimplementedError("Network support requires JSON library");
296 }
297 absl::Status SubmitProposal(const std::string&, const std::string&,
298 const std::string&) {
299 return absl::UnimplementedError("Network support requires JSON library");
300 }
301 absl::StatusOr<std::string> GetProposalStatus(const std::string&) {
302 return absl::UnimplementedError("Network support requires JSON library");
303 }
304 absl::StatusOr<bool> WaitForApproval(const std::string&, int) {
305 return absl::UnimplementedError("Network support requires JSON library");
306 }
307 absl::Status SendMessage(const std::string&, const std::string&) {
308 return absl::UnimplementedError("Network support requires JSON library");
309 }
310 absl::StatusOr<std::string> QueryAI(const std::string&, const std::string&) {
311 return absl::UnimplementedError("Network support requires JSON library");
312 }
313 bool IsConnected() const { return false; }
314 std::string GetLastProposalId() const { return ""; }
315};
316
317#endif // YAZE_WITH_JSON
318
319// ============================================================================
320// Z3edNetworkClient Implementation
321// ============================================================================
322
323Z3edNetworkClient::Z3edNetworkClient() : impl_(std::make_unique<Impl>()) {}
324
326
327absl::Status Z3edNetworkClient::Connect(const std::string& host, int port) {
328 return impl_->Connect(host, port);
329}
330
331absl::Status Z3edNetworkClient::JoinSession(const std::string& session_code,
332 const std::string& username) {
333 return impl_->JoinSession(session_code, username);
334}
335
336absl::Status Z3edNetworkClient::SubmitProposal(const std::string& description,
337 const std::string& proposal_json,
338 const std::string& username) {
339 return impl_->SubmitProposal(description, proposal_json, username);
340}
341
342absl::StatusOr<std::string> Z3edNetworkClient::GetProposalStatus(
343 const std::string& proposal_id) {
344 return impl_->GetProposalStatus(proposal_id);
345}
346
348 const std::string& proposal_id, int timeout_seconds) {
349 return impl_->WaitForApproval(proposal_id, timeout_seconds);
350}
351
352absl::Status Z3edNetworkClient::SendMessage(const std::string& message,
353 const std::string& sender) {
354 return impl_->SendMessage(message, sender);
355}
356
357absl::StatusOr<std::string> Z3edNetworkClient::QueryAI(
358 const std::string& query, const std::string& username) {
359 return impl_->QueryAI(query, username);
360}
361
363 impl_->Disconnect();
364}
365
367 return impl_->IsConnected();
368}
369
370} // namespace net
371} // namespace cli
372} // namespace yaze
absl::Status SendMessage(const std::string &, const std::string &)
absl::Status Connect(const std::string &, int)
absl::StatusOr< bool > WaitForApproval(const std::string &, int)
absl::Status JoinSession(const std::string &, const std::string &)
absl::StatusOr< std::string > GetProposalStatus(const std::string &)
absl::Status SubmitProposal(const std::string &, const std::string &, const std::string &)
absl::StatusOr< std::string > QueryAI(const std::string &, const std::string &)
absl::StatusOr< bool > WaitForApproval(const std::string &proposal_id, int timeout_seconds=60)
absl::Status SendMessage(const std::string &message, const std::string &sender)
absl::Status JoinSession(const std::string &session_code, const std::string &username)
absl::StatusOr< std::string > QueryAI(const std::string &query, const std::string &username)
absl::Status Connect(const std::string &host, int port=8765)
absl::StatusOr< std::string > GetProposalStatus(const std::string &proposal_id)
absl::Status SubmitProposal(const std::string &description, const std::string &proposal_json, const std::string &username)