yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
canvas_automation_service.cc
Go to the documentation of this file.
2
3#ifdef YAZE_WITH_GRPC
4
6
7#include <grpcpp/grpcpp.h>
8
12#include "protos/canvas_automation.grpc.pb.h"
13#include "protos/canvas_automation.pb.h"
14
15namespace yaze {
16
17namespace {
18
19// Helper to convert absl::Status to grpc::Status
20grpc::Status ConvertStatus(const absl::Status& status) {
21 if (status.ok()) {
22 return grpc::Status::OK;
23 }
24
25 grpc::StatusCode code;
26 switch (status.code()) {
27 case absl::StatusCode::kNotFound:
28 code = grpc::StatusCode::NOT_FOUND;
29 break;
30 case absl::StatusCode::kInvalidArgument:
31 code = grpc::StatusCode::INVALID_ARGUMENT;
32 break;
33 case absl::StatusCode::kFailedPrecondition:
34 code = grpc::StatusCode::FAILED_PRECONDITION;
35 break;
36 case absl::StatusCode::kOutOfRange:
37 code = grpc::StatusCode::OUT_OF_RANGE;
38 break;
39 case absl::StatusCode::kUnimplemented:
40 code = grpc::StatusCode::UNIMPLEMENTED;
41 break;
42 case absl::StatusCode::kInternal:
43 code = grpc::StatusCode::INTERNAL;
44 break;
45 case absl::StatusCode::kUnavailable:
46 code = grpc::StatusCode::UNAVAILABLE;
47 break;
48 default:
49 code = grpc::StatusCode::UNKNOWN;
50 break;
51 }
52
53 return grpc::Status(code, std::string(status.message().data(), status.message().size()));
54}
55
56} // namespace
57
58void CanvasAutomationServiceImpl::RegisterCanvas(const std::string& canvas_id,
59 gui::Canvas* canvas) {
60 canvases_[canvas_id] = canvas;
61}
62
63void CanvasAutomationServiceImpl::RegisterOverworldEditor(
64 const std::string& canvas_id, editor::OverworldEditor* editor) {
65 overworld_editors_[canvas_id] = editor;
66}
67
68gui::Canvas* CanvasAutomationServiceImpl::GetCanvas(
69 const std::string& canvas_id) {
70 auto it = canvases_.find(canvas_id);
71 if (it != canvases_.end()) {
72 return it->second;
73 }
74 return nullptr;
75}
76
77editor::OverworldEditor* CanvasAutomationServiceImpl::GetOverworldEditor(
78 const std::string& canvas_id) {
79 auto it = overworld_editors_.find(canvas_id);
80 if (it != overworld_editors_.end()) {
81 return it->second;
82 }
83 return nullptr;
84}
85
86// ============================================================================
87// Tile Operations
88// ============================================================================
89
90absl::Status CanvasAutomationServiceImpl::SetTile(
91 const proto::SetTileRequest* request, proto::SetTileResponse* response) {
92 auto* canvas = GetCanvas(request->canvas_id());
93 if (!canvas) {
94 response->set_success(false);
95 response->set_error("Canvas not found: " + request->canvas_id());
96 return absl::NotFoundError(response->error());
97 }
98
99 auto* api = canvas->GetAutomationAPI();
100 bool success = api->SetTileAt(request->x(), request->y(), request->tile_id());
101
102 response->set_success(success);
103 if (!success) {
104 response->set_error(
105 "Failed to set tile - out of bounds or callback failed");
106 }
107
108 return absl::OkStatus();
109}
110
111absl::Status CanvasAutomationServiceImpl::GetTile(
112 const proto::GetTileRequest* request, proto::GetTileResponse* response) {
113 auto* canvas = GetCanvas(request->canvas_id());
114 if (!canvas) {
115 response->set_success(false);
116 response->set_error("Canvas not found: " + request->canvas_id());
117 return absl::NotFoundError(response->error());
118 }
119
120 auto* api = canvas->GetAutomationAPI();
121 int tile_id = api->GetTileAt(request->x(), request->y());
122
123 if (tile_id >= 0) {
124 response->set_tile_id(tile_id);
125 response->set_success(true);
126 } else {
127 response->set_success(false);
128 response->set_error("Tile not found - out of bounds or no callback set");
129 }
130
131 return absl::OkStatus();
132}
133
134absl::Status CanvasAutomationServiceImpl::SetTiles(
135 const proto::SetTilesRequest* request, proto::SetTilesResponse* response) {
136 auto* canvas = GetCanvas(request->canvas_id());
137 if (!canvas) {
138 response->set_success(false);
139 response->set_error("Canvas not found: " + request->canvas_id());
140 return absl::NotFoundError(response->error());
141 }
142
143 auto* api = canvas->GetAutomationAPI();
144
145 std::vector<std::tuple<int, int, int>> tiles;
146 for (const auto& tile : request->tiles()) {
147 tiles.push_back({tile.x(), tile.y(), tile.tile_id()});
148 }
149
150 bool success = api->SetTiles(tiles);
151 response->set_success(success);
152 response->set_tiles_painted(tiles.size());
153
154 return absl::OkStatus();
155}
156
157// ============================================================================
158// Selection Operations
159// ============================================================================
160
161absl::Status CanvasAutomationServiceImpl::SelectTile(
162 const proto::SelectTileRequest* request,
163 proto::SelectTileResponse* response) {
164 auto* canvas = GetCanvas(request->canvas_id());
165 if (!canvas) {
166 response->set_success(false);
167 response->set_error("Canvas not found: " + request->canvas_id());
168 return absl::NotFoundError(response->error());
169 }
170
171 auto* api = canvas->GetAutomationAPI();
172 api->SelectTile(request->x(), request->y());
173 response->set_success(true);
174
175 return absl::OkStatus();
176}
177
178absl::Status CanvasAutomationServiceImpl::SelectTileRect(
179 const proto::SelectTileRectRequest* request,
180 proto::SelectTileRectResponse* response) {
181 auto* canvas = GetCanvas(request->canvas_id());
182 if (!canvas) {
183 response->set_success(false);
184 response->set_error("Canvas not found: " + request->canvas_id());
185 return absl::NotFoundError(response->error());
186 }
187
188 auto* api = canvas->GetAutomationAPI();
189 const auto& rect = request->rect();
190 api->SelectTileRect(rect.x1(), rect.y1(), rect.x2(), rect.y2());
191
192 auto selection = api->GetSelection();
193 response->set_success(true);
194 response->set_tiles_selected(selection.selected_tiles.size());
195
196 return absl::OkStatus();
197}
198
199absl::Status CanvasAutomationServiceImpl::GetSelection(
200 const proto::GetSelectionRequest* request,
201 proto::GetSelectionResponse* response) {
202 auto* canvas = GetCanvas(request->canvas_id());
203 if (!canvas) {
204 return absl::NotFoundError("Canvas not found: " + request->canvas_id());
205 }
206
207 auto* api = canvas->GetAutomationAPI();
208 auto selection = api->GetSelection();
209
210 response->set_has_selection(selection.has_selection);
211
212 for (const auto& tile : selection.selected_tiles) {
213 auto* coord = response->add_selected_tiles();
214 coord->set_x(static_cast<int>(tile.x));
215 coord->set_y(static_cast<int>(tile.y));
216 }
217
218 auto* start = response->mutable_selection_start();
219 start->set_x(static_cast<int>(selection.selection_start.x));
220 start->set_y(static_cast<int>(selection.selection_start.y));
221
222 auto* end = response->mutable_selection_end();
223 end->set_x(static_cast<int>(selection.selection_end.x));
224 end->set_y(static_cast<int>(selection.selection_end.y));
225
226 return absl::OkStatus();
227}
228
229absl::Status CanvasAutomationServiceImpl::ClearSelection(
230 const proto::ClearSelectionRequest* request,
231 proto::ClearSelectionResponse* response) {
232 auto* canvas = GetCanvas(request->canvas_id());
233 if (!canvas) {
234 response->set_success(false);
235 return absl::NotFoundError("Canvas not found: " + request->canvas_id());
236 }
237
238 auto* api = canvas->GetAutomationAPI();
239 api->ClearSelection();
240 response->set_success(true);
241
242 return absl::OkStatus();
243}
244
245// ============================================================================
246// View Operations
247// ============================================================================
248
249absl::Status CanvasAutomationServiceImpl::ScrollToTile(
250 const proto::ScrollToTileRequest* request,
251 proto::ScrollToTileResponse* response) {
252 auto* canvas = GetCanvas(request->canvas_id());
253 if (!canvas) {
254 response->set_success(false);
255 response->set_error("Canvas not found: " + request->canvas_id());
256 return absl::NotFoundError(response->error());
257 }
258
259 auto* api = canvas->GetAutomationAPI();
260 api->ScrollToTile(request->x(), request->y(), request->center());
261 response->set_success(true);
262
263 return absl::OkStatus();
264}
265
266absl::Status CanvasAutomationServiceImpl::CenterOn(
267 const proto::CenterOnRequest* request, proto::CenterOnResponse* response) {
268 auto* canvas = GetCanvas(request->canvas_id());
269 if (!canvas) {
270 response->set_success(false);
271 response->set_error("Canvas not found: " + request->canvas_id());
272 return absl::NotFoundError(response->error());
273 }
274
275 auto* api = canvas->GetAutomationAPI();
276 api->CenterOn(request->x(), request->y());
277 response->set_success(true);
278
279 return absl::OkStatus();
280}
281
282absl::Status CanvasAutomationServiceImpl::SetZoom(
283 const proto::SetZoomRequest* request, proto::SetZoomResponse* response) {
284 auto* canvas = GetCanvas(request->canvas_id());
285 if (!canvas) {
286 response->set_success(false);
287 response->set_error("Canvas not found: " + request->canvas_id());
288 return absl::NotFoundError(response->error());
289 }
290
291 auto* api = canvas->GetAutomationAPI();
292 api->SetZoom(request->zoom());
293
294 float actual_zoom = api->GetZoom();
295 response->set_success(true);
296 response->set_actual_zoom(actual_zoom);
297
298 return absl::OkStatus();
299}
300
301absl::Status CanvasAutomationServiceImpl::GetZoom(
302 const proto::GetZoomRequest* request, proto::GetZoomResponse* response) {
303 auto* canvas = GetCanvas(request->canvas_id());
304 if (!canvas) {
305 return absl::NotFoundError("Canvas not found: " + request->canvas_id());
306 }
307
308 auto* api = canvas->GetAutomationAPI();
309 response->set_zoom(api->GetZoom());
310
311 return absl::OkStatus();
312}
313
314// ============================================================================
315// Query Operations
316// ============================================================================
317
318absl::Status CanvasAutomationServiceImpl::GetDimensions(
319 const proto::GetDimensionsRequest* request,
320 proto::GetDimensionsResponse* response) {
321 auto* canvas = GetCanvas(request->canvas_id());
322 if (!canvas) {
323 return absl::NotFoundError("Canvas not found: " + request->canvas_id());
324 }
325
326 auto* api = canvas->GetAutomationAPI();
327 auto dims = api->GetDimensions();
328
329 auto* proto_dims = response->mutable_dimensions();
330 proto_dims->set_width_tiles(dims.width_tiles);
331 proto_dims->set_height_tiles(dims.height_tiles);
332 proto_dims->set_tile_size(dims.tile_size);
333
334 return absl::OkStatus();
335}
336
337absl::Status CanvasAutomationServiceImpl::GetVisibleRegion(
338 const proto::GetVisibleRegionRequest* request,
339 proto::GetVisibleRegionResponse* response) {
340 auto* canvas = GetCanvas(request->canvas_id());
341 if (!canvas) {
342 return absl::NotFoundError("Canvas not found: " + request->canvas_id());
343 }
344
345 auto* api = canvas->GetAutomationAPI();
346 auto region = api->GetVisibleRegion();
347
348 auto* proto_region = response->mutable_region();
349 proto_region->set_min_x(region.min_x);
350 proto_region->set_min_y(region.min_y);
351 proto_region->set_max_x(region.max_x);
352 proto_region->set_max_y(region.max_y);
353
354 return absl::OkStatus();
355}
356
357absl::Status CanvasAutomationServiceImpl::IsTileVisible(
358 const proto::IsTileVisibleRequest* request,
359 proto::IsTileVisibleResponse* response) {
360 auto* canvas = GetCanvas(request->canvas_id());
361 if (!canvas) {
362 return absl::NotFoundError("Canvas not found: " + request->canvas_id());
363 }
364
365 auto* api = canvas->GetAutomationAPI();
366 response->set_is_visible(api->IsTileVisible(request->x(), request->y()));
367
368 return absl::OkStatus();
369}
370
371// ============================================================================
372// Coordinate Mapping Operations
373// ============================================================================
374
375absl::Status CanvasAutomationServiceImpl::ScreenToTile(
376 const proto::ScreenToTileRequest* request,
377 proto::ScreenToTileResponse* response) {
378 auto* canvas = GetCanvas(request->canvas_id());
379 if (!canvas) {
380 response->set_success(false);
381 response->set_error("Canvas not found: " + request->canvas_id());
382 return absl::NotFoundError(response->error());
383 }
384
385 // Create a CoordinateMapper and configure it from canvas state
386 gui::CoordinateMapper mapper;
387 mapper.SetCanvasId(request->canvas_id());
388
389 // Get canvas geometry from current state
390 gui::CanvasGeometry geometry;
391 geometry.canvas_p0 = canvas->zero_point();
392 geometry.canvas_sz = canvas->canvas_size();
393 geometry.canvas_p1 = ImVec2(geometry.canvas_p0.x + geometry.canvas_sz.x,
394 geometry.canvas_p0.y + geometry.canvas_sz.y);
395 geometry.scrolling = canvas->scrolling();
396 mapper.SetGeometry(geometry);
397 mapper.SetScale(canvas->global_scale());
398
399 // Configure tile settings from canvas
400 gui::CoordinateMapperConfig config;
401 config.tile_size = canvas->GetGridStep();
402 config.tiles_per_row = static_cast<int>(canvas->canvas_size().x / config.tile_size);
403 config.tiles_per_col = static_cast<int>(canvas->canvas_size().y / config.tile_size);
404 config.use_tile16 = (config.tile_size == 16.0f);
405 mapper.SetConfig(config);
406
407 // Perform the coordinate mapping
408 auto result = mapper.ScreenToTile(request->screen_x(), request->screen_y());
409
410 // Populate response
411 response->set_success(true);
412 response->set_screen_x(result.screen_x);
413 response->set_screen_y(result.screen_y);
414 response->set_canvas_x(result.canvas_x);
415 response->set_canvas_y(result.canvas_y);
416 response->set_content_x(result.content_x);
417 response->set_content_y(result.content_y);
418 response->set_in_canvas_bounds(result.in_canvas_bounds);
419
420 // Populate tile hit info
421 auto* tile_info = response->mutable_tile_info();
422 tile_info->set_tile_id(result.tile_info.tile_id);
423 tile_info->set_tile16_index(result.tile_info.tile16_index);
424 tile_info->set_rom_offset(result.tile_info.rom_offset);
425 tile_info->set_palette_group(result.tile_info.palette_group);
426 tile_info->set_palette_index(result.tile_info.palette_index);
427 tile_info->set_tile_origin_x(result.tile_info.tile_origin_px.x);
428 tile_info->set_tile_origin_y(result.tile_info.tile_origin_px.y);
429 tile_info->set_tile_width(result.tile_info.tile_size_px.x);
430 tile_info->set_tile_height(result.tile_info.tile_size_px.y);
431 tile_info->set_is_valid(result.tile_info.is_valid);
432 tile_info->set_map_x(result.tile_info.map_x);
433 tile_info->set_map_y(result.tile_info.map_y);
434 tile_info->set_local_map_id(result.tile_info.local_map_id);
435
436 return absl::OkStatus();
437}
438
439// ============================================================================
440// gRPC Service Wrapper
441// ============================================================================
442
450class CanvasAutomationServiceGrpc final
451 : public proto::CanvasAutomation::Service {
452 public:
453 explicit CanvasAutomationServiceGrpc(CanvasAutomationServiceImpl* impl)
454 : impl_(impl) {}
455
456 // Tile Operations
457 grpc::Status SetTile(grpc::ServerContext* context,
458 const proto::SetTileRequest* request,
459 proto::SetTileResponse* response) override {
460 return ConvertStatus(impl_->SetTile(request, response));
461 }
462
463 grpc::Status GetTile(grpc::ServerContext* context,
464 const proto::GetTileRequest* request,
465 proto::GetTileResponse* response) override {
466 return ConvertStatus(impl_->GetTile(request, response));
467 }
468
469 grpc::Status SetTiles(grpc::ServerContext* context,
470 const proto::SetTilesRequest* request,
471 proto::SetTilesResponse* response) override {
472 return ConvertStatus(impl_->SetTiles(request, response));
473 }
474
475 // Selection Operations
476 grpc::Status SelectTile(grpc::ServerContext* context,
477 const proto::SelectTileRequest* request,
478 proto::SelectTileResponse* response) override {
479 return ConvertStatus(impl_->SelectTile(request, response));
480 }
481
482 grpc::Status SelectTileRect(
483 grpc::ServerContext* context, const proto::SelectTileRectRequest* request,
484 proto::SelectTileRectResponse* response) override {
485 return ConvertStatus(impl_->SelectTileRect(request, response));
486 }
487
488 grpc::Status GetSelection(grpc::ServerContext* context,
489 const proto::GetSelectionRequest* request,
490 proto::GetSelectionResponse* response) override {
491 return ConvertStatus(impl_->GetSelection(request, response));
492 }
493
494 grpc::Status ClearSelection(
495 grpc::ServerContext* context, const proto::ClearSelectionRequest* request,
496 proto::ClearSelectionResponse* response) override {
497 return ConvertStatus(impl_->ClearSelection(request, response));
498 }
499
500 // View Operations
501 grpc::Status ScrollToTile(grpc::ServerContext* context,
502 const proto::ScrollToTileRequest* request,
503 proto::ScrollToTileResponse* response) override {
504 return ConvertStatus(impl_->ScrollToTile(request, response));
505 }
506
507 grpc::Status CenterOn(grpc::ServerContext* context,
508 const proto::CenterOnRequest* request,
509 proto::CenterOnResponse* response) override {
510 return ConvertStatus(impl_->CenterOn(request, response));
511 }
512
513 grpc::Status SetZoom(grpc::ServerContext* context,
514 const proto::SetZoomRequest* request,
515 proto::SetZoomResponse* response) override {
516 return ConvertStatus(impl_->SetZoom(request, response));
517 }
518
519 grpc::Status GetZoom(grpc::ServerContext* context,
520 const proto::GetZoomRequest* request,
521 proto::GetZoomResponse* response) override {
522 return ConvertStatus(impl_->GetZoom(request, response));
523 }
524
525 // Query Operations
526 grpc::Status GetDimensions(grpc::ServerContext* context,
527 const proto::GetDimensionsRequest* request,
528 proto::GetDimensionsResponse* response) override {
529 return ConvertStatus(impl_->GetDimensions(request, response));
530 }
531
532 grpc::Status GetVisibleRegion(
533 grpc::ServerContext* context,
534 const proto::GetVisibleRegionRequest* request,
535 proto::GetVisibleRegionResponse* response) override {
536 return ConvertStatus(impl_->GetVisibleRegion(request, response));
537 }
538
539 grpc::Status IsTileVisible(grpc::ServerContext* context,
540 const proto::IsTileVisibleRequest* request,
541 proto::IsTileVisibleResponse* response) override {
542 return ConvertStatus(impl_->IsTileVisible(request, response));
543 }
544
545 // Coordinate Mapping Operations
546 grpc::Status ScreenToTile(grpc::ServerContext* context,
547 const proto::ScreenToTileRequest* request,
548 proto::ScreenToTileResponse* response) override {
549 return ConvertStatus(impl_->ScreenToTile(request, response));
550 }
551
552 private:
553 CanvasAutomationServiceImpl* impl_;
554};
555
556// Factory function to create the gRPC wrapper
557// Returns as base grpc::Service* to avoid incomplete type issues in headers
558std::unique_ptr<grpc::Service> CreateCanvasAutomationServiceGrpc(
559 CanvasAutomationServiceImpl* impl) {
560 return std::make_unique<CanvasAutomationServiceGrpc>(impl);
561}
562
563} // namespace yaze
564
565#endif // YAZE_WITH_GRPC