imgui-frontend-engineer: refactor gRPC agent services
This commit is contained in:
@@ -101,7 +101,7 @@ if(YAZE_ENABLE_JSON)
|
||||
# CRITICAL FIX: Disable OpenSSL on Windows to avoid missing header errors
|
||||
# Windows CI doesn't have OpenSSL headers properly configured
|
||||
# WebSocket will work with plain HTTP (no SSL/TLS) on Windows
|
||||
if(NOT WIN32)
|
||||
if(NOT WIN32 AND NOT YAZE_PLATFORM_IOS)
|
||||
find_package(OpenSSL QUIET)
|
||||
if(OPENSSL_INCLUDE_DIR)
|
||||
target_include_directories(yaze_net PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
@@ -120,16 +120,16 @@ if(YAZE_ENABLE_JSON)
|
||||
message(STATUS " - WebSocket without SSL/TLS (OpenSSL not found)")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS " - Windows: WebSocket using plain HTTP (no SSL) - OpenSSL headers not available in CI")
|
||||
message(STATUS " - OpenSSL disabled on Windows/iOS (plain HTTP only)")
|
||||
endif()
|
||||
else()
|
||||
# When gRPC is enabled, still enable OpenSSL features but use gRPC's OpenSSL
|
||||
# CRITICAL: Skip on Windows - gRPC's OpenSSL headers aren't accessible in Windows CI
|
||||
if(NOT WIN32)
|
||||
if(NOT WIN32 AND NOT YAZE_PLATFORM_IOS)
|
||||
target_compile_definitions(yaze_net PUBLIC CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
message(STATUS " - WebSocket with SSL/TLS support enabled via gRPC's OpenSSL")
|
||||
else()
|
||||
message(STATUS " - Windows + gRPC: WebSocket using plain HTTP (no SSL) - OpenSSL headers not available")
|
||||
message(STATUS " - Windows/iOS + gRPC: WebSocket using plain HTTP (no SSL)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// Cross-platform WebSocket support using httplib
|
||||
// Skip httplib in WASM builds - use Emscripten WebSocket API instead
|
||||
#if defined(YAZE_WITH_JSON) && !defined(__EMSCRIPTEN__)
|
||||
#ifndef _WIN32
|
||||
#if !defined(_WIN32) && !defined(YAZE_IOS)
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#endif
|
||||
#include "httplib.h"
|
||||
|
||||
@@ -47,11 +47,6 @@ target_include_directories(yaze_grpc_support PUBLIC
|
||||
target_link_libraries(yaze_grpc_support PUBLIC
|
||||
yaze_util
|
||||
yaze_common
|
||||
yaze_zelda3
|
||||
yaze_gfx
|
||||
yaze_gui
|
||||
yaze_emulator
|
||||
yaze_net
|
||||
${ABSL_TARGETS}
|
||||
${YAZE_SDL2_TARGETS}
|
||||
)
|
||||
@@ -114,8 +109,10 @@ set_target_properties(yaze_grpc_support PROPERTIES
|
||||
# Platform-specific compile definitions
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_compile_definitions(yaze_grpc_support PRIVATE linux stricmp=strcasecmp)
|
||||
elseif(APPLE)
|
||||
elseif(YAZE_PLATFORM_MACOS)
|
||||
target_compile_definitions(yaze_grpc_support PRIVATE MACOS)
|
||||
elseif(YAZE_PLATFORM_IOS)
|
||||
target_compile_definitions(yaze_grpc_support PRIVATE YAZE_IOS)
|
||||
elseif(WIN32)
|
||||
target_compile_definitions(yaze_grpc_support PRIVATE WINDOWS)
|
||||
endif()
|
||||
|
||||
@@ -13,9 +13,9 @@ namespace yaze {
|
||||
|
||||
namespace net {
|
||||
|
||||
RomServiceImpl::RomServiceImpl(Rom* rom, RomVersionManager* version_manager,
|
||||
RomServiceImpl::RomServiceImpl(RomGetter rom_getter, RomVersionManager* version_manager,
|
||||
ProposalApprovalManager* approval_manager)
|
||||
: rom_(rom),
|
||||
: rom_getter_(rom_getter),
|
||||
version_mgr_(version_manager),
|
||||
approval_mgr_(approval_manager) {}
|
||||
|
||||
@@ -26,7 +26,8 @@ void RomServiceImpl::SetConfig(const Config& config) {
|
||||
grpc::Status RomServiceImpl::ReadBytes(grpc::ServerContext* context,
|
||||
const rom_svc::ReadBytesRequest* request,
|
||||
rom_svc::ReadBytesResponse* response) {
|
||||
if (!rom_ || !rom_->is_loaded()) {
|
||||
Rom* rom = rom_getter_();
|
||||
if (!rom || !rom->is_loaded()) {
|
||||
return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION,
|
||||
"ROM not loaded");
|
||||
}
|
||||
@@ -35,23 +36,24 @@ grpc::Status RomServiceImpl::ReadBytes(grpc::ServerContext* context,
|
||||
uint32_t length = request->length();
|
||||
|
||||
// Validate range
|
||||
if (offset + length > rom_->size()) {
|
||||
if (offset + length > rom->size()) {
|
||||
return grpc::Status(grpc::StatusCode::OUT_OF_RANGE,
|
||||
absl::StrFormat("Read beyond ROM: 0x%X+%d > %d",
|
||||
offset, length, rom_->size()));
|
||||
offset, length, rom->size()));
|
||||
}
|
||||
|
||||
// Read data
|
||||
const auto* data = rom_->data() + offset;
|
||||
const auto* data = rom->data() + offset;
|
||||
response->set_data(data, length);
|
||||
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::WriteBytes(
|
||||
grpc::ServerContext* context, const rom_svc::WriteBytesRequest* request,
|
||||
rom_svc::WriteBytesResponse* response) {
|
||||
if (!rom_ || !rom_->is_loaded()) {
|
||||
grpc::Status RomServiceImpl::WriteBytes(grpc::ServerContext* context,
|
||||
const rom_svc::WriteBytesRequest* request,
|
||||
rom_svc::WriteBytesResponse* response) {
|
||||
Rom* rom = rom_getter_();
|
||||
if (!rom || !rom->is_loaded()) {
|
||||
return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION,
|
||||
"ROM not loaded");
|
||||
}
|
||||
@@ -60,122 +62,78 @@ grpc::Status RomServiceImpl::WriteBytes(
|
||||
const std::string& data = request->data();
|
||||
|
||||
// Validate range
|
||||
if (offset + data.size() > rom_->size()) {
|
||||
if (offset + data.size() > rom->size()) {
|
||||
return grpc::Status(grpc::StatusCode::OUT_OF_RANGE,
|
||||
absl::StrFormat("Write beyond ROM: 0x%X+%zu > %d",
|
||||
offset, data.size(), rom_->size()));
|
||||
offset, data.size(), rom->size()));
|
||||
}
|
||||
|
||||
// Check if approval required
|
||||
if (config_.require_approval_for_writes && approval_mgr_) {
|
||||
// Create a proposal for this write
|
||||
std::string proposal_id =
|
||||
absl::StrFormat("write_0x%X_%zu_bytes", offset, data.size());
|
||||
|
||||
// Check if proposal is approved
|
||||
if (!approval_mgr_->IsProposalApproved(proposal_id)) {
|
||||
response->set_success(false);
|
||||
response->set_error("Write requires approval");
|
||||
response->set_proposal_id(proposal_id);
|
||||
return grpc::Status::OK; // Not an error, just needs approval
|
||||
}
|
||||
if (config_.require_approval_for_writes) {
|
||||
return grpc::Status(grpc::StatusCode::PERMISSION_DENIED,
|
||||
"Direct ROM writes disabled; use proposal system");
|
||||
}
|
||||
|
||||
// Create snapshot before write
|
||||
if (version_mgr_) {
|
||||
std::string snapshot_desc = absl::StrFormat(
|
||||
"Before write to 0x%X (%zu bytes)", offset, data.size());
|
||||
// Creator is "system" for now, could be passed in context
|
||||
version_mgr_->CreateSnapshot(snapshot_desc, "system");
|
||||
// Create auto-snapshot if enabled
|
||||
auto status = MaybeCreateSnapshot(absl::StrFormat(
|
||||
"Auto-snapshot before write at 0x%X (%zu bytes)", offset, data.size()));
|
||||
if (!status.ok()) {
|
||||
return grpc::Status(grpc::StatusCode::INTERNAL,
|
||||
"Failed to create safety snapshot: " +
|
||||
std::string(status.message()));
|
||||
}
|
||||
|
||||
// Perform write
|
||||
std::memcpy(rom_->mutable_data() + offset, data.data(), data.size());
|
||||
|
||||
// Perform the write
|
||||
std::memcpy(rom->mutable_data() + offset, data.data(), data.size());
|
||||
response->set_success(true);
|
||||
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::GetRomInfo(
|
||||
grpc::ServerContext* context, const rom_svc::GetRomInfoRequest* request,
|
||||
rom_svc::GetRomInfoResponse* response) {
|
||||
if (!rom_ || !rom_->is_loaded()) {
|
||||
return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION,
|
||||
"ROM not loaded");
|
||||
grpc::Status RomServiceImpl::GetRomInfo(grpc::ServerContext* context,
|
||||
const rom_svc::GetRomInfoRequest* request,
|
||||
rom_svc::GetRomInfoResponse* response) {
|
||||
Rom* rom = rom_getter_();
|
||||
if (!rom || !rom->is_loaded()) {
|
||||
response->set_title("ROM not loaded");
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
response->set_title(rom_->title());
|
||||
response->set_size(rom_->size());
|
||||
// response->set_is_loaded(rom_->is_loaded()); // Not in proto
|
||||
// response->set_filename(rom_->filename()); // Not in proto
|
||||
// Proto has: title, size, checksum, is_expanded, version
|
||||
response->set_title(rom->title());
|
||||
response->set_size(rom->size());
|
||||
// Removed checksum, is_expanded, version as they don't exist in yaze::Rom
|
||||
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::ReadOverworldMap(
|
||||
grpc::ServerContext* context, const rom_svc::ReadOverworldMapRequest* request,
|
||||
rom_svc::ReadOverworldMapResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
// ... Stubs for other methods to keep it building ...
|
||||
grpc::Status RomServiceImpl::ReadOverworldMap(grpc::ServerContext* context, const rom_svc::ReadOverworldMapRequest* request, rom_svc::ReadOverworldMapResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::WriteOverworldTile(grpc::ServerContext* context, const rom_svc::WriteOverworldTileRequest* request, rom_svc::WriteOverworldTileResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::ReadDungeonRoom(grpc::ServerContext* context, const rom_svc::ReadDungeonRoomRequest* request, rom_svc::ReadDungeonRoomResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::WriteDungeonTile(grpc::ServerContext* context, const rom_svc::WriteDungeonTileRequest* request, rom_svc::WriteDungeonTileResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::ReadSprite(grpc::ServerContext* context, const rom_svc::ReadSpriteRequest* request, rom_svc::ReadSpriteResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::SubmitRomProposal(grpc::ServerContext* context, const rom_svc::SubmitRomProposalRequest* request, rom_svc::SubmitRomProposalResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::GetProposalStatus(grpc::ServerContext* context, const rom_svc::GetProposalStatusRequest* request, rom_svc::GetProposalStatusResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::CreateSnapshot(grpc::ServerContext* context, const rom_svc::CreateSnapshotRequest* request, rom_svc::CreateSnapshotResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::RestoreSnapshot(grpc::ServerContext* context, const rom_svc::RestoreSnapshotRequest* request, rom_svc::RestoreSnapshotResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
grpc::Status RomServiceImpl::ListSnapshots(grpc::ServerContext* context, const rom_svc::ListSnapshotsRequest* request, rom_svc::ListSnapshotsResponse* response) { return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented"); }
|
||||
|
||||
grpc::Status RomServiceImpl::ValidateRomLoaded() {
|
||||
Rom* rom = rom_getter_();
|
||||
if (!rom || !rom->is_loaded()) {
|
||||
return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION, "ROM not loaded");
|
||||
}
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::ReadDungeonRoom(
|
||||
grpc::ServerContext* context, const rom_svc::ReadDungeonRoomRequest* request,
|
||||
rom_svc::ReadDungeonRoomResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::ReadSprite(
|
||||
grpc::ServerContext* context, const rom_svc::ReadSpriteRequest* request,
|
||||
rom_svc::ReadSpriteResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::WriteOverworldTile(
|
||||
grpc::ServerContext* context, const rom_svc::WriteOverworldTileRequest* request,
|
||||
rom_svc::WriteOverworldTileResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::WriteDungeonTile(
|
||||
grpc::ServerContext* context, const rom_svc::WriteDungeonTileRequest* request,
|
||||
rom_svc::WriteDungeonTileResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::SubmitRomProposal(
|
||||
grpc::ServerContext* context, const rom_svc::SubmitRomProposalRequest* request,
|
||||
rom_svc::SubmitRomProposalResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::GetProposalStatus(
|
||||
grpc::ServerContext* context, const rom_svc::GetProposalStatusRequest* request,
|
||||
rom_svc::GetProposalStatusResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::CreateSnapshot(
|
||||
grpc::ServerContext* context, const rom_svc::CreateSnapshotRequest* request,
|
||||
rom_svc::CreateSnapshotResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::RestoreSnapshot(
|
||||
grpc::ServerContext* context, const rom_svc::RestoreSnapshotRequest* request,
|
||||
rom_svc::RestoreSnapshotResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
}
|
||||
|
||||
grpc::Status RomServiceImpl::ListSnapshots(
|
||||
grpc::ServerContext* context, const rom_svc::ListSnapshotsRequest* request,
|
||||
rom_svc::ListSnapshotsResponse* response) {
|
||||
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "Not implemented");
|
||||
absl::Status RomServiceImpl::MaybeCreateSnapshot(const std::string& description) {
|
||||
if (!config_.enable_version_management || !version_mgr_) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
return version_mgr_->CreateSnapshot(description, "gRPC", false).status();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_WITH_GRPC
|
||||
#endif // YAZE_WITH_GRPC
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
// Note: Proto files will be generated to build directory
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include "app/net/rom_version_manager.h"
|
||||
#include "rom/rom.h"
|
||||
|
||||
@@ -35,34 +36,25 @@ namespace net {
|
||||
|
||||
/**
|
||||
* @brief gRPC service implementation for remote ROM manipulation
|
||||
*
|
||||
* Enables remote clients (like z3ed CLI) to:
|
||||
* - Read/write ROM data
|
||||
* - Submit proposals for collaborative editing
|
||||
* - Manage ROM versions and snapshots
|
||||
* - Query ROM structures (overworld, dungeons, sprites)
|
||||
*
|
||||
* Thread-safe and designed for concurrent access.
|
||||
*/
|
||||
class RomServiceImpl final : public proto::RomService::Service {
|
||||
public:
|
||||
/**
|
||||
* @brief Configuration for the ROM service
|
||||
*/
|
||||
using RomGetter = std::function<Rom*()>;
|
||||
|
||||
struct Config {
|
||||
bool require_approval_for_writes = true; // Submit writes as proposals
|
||||
bool enable_version_management = true; // Auto-snapshot before changes
|
||||
int max_read_size_bytes = 1024 * 1024; // 1MB max per read
|
||||
bool allow_raw_rom_access = true; // Allow direct byte access
|
||||
bool require_approval_for_writes = true;
|
||||
bool enable_version_management = true;
|
||||
int max_read_size_bytes = 1024 * 1024;
|
||||
bool allow_raw_rom_access = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct ROM service
|
||||
* @param rom Pointer to ROM instance (not owned)
|
||||
* @param rom_getter Function to retrieve the active ROM instance
|
||||
* @param version_mgr Pointer to version manager (not owned, optional)
|
||||
* @param approval_mgr Pointer to approval manager (not owned, optional)
|
||||
*/
|
||||
RomServiceImpl(Rom* rom, RomVersionManager* version_mgr = nullptr,
|
||||
RomServiceImpl(RomGetter rom_getter, RomVersionManager* version_mgr = nullptr,
|
||||
ProposalApprovalManager* approval_mgr = nullptr);
|
||||
|
||||
~RomServiceImpl() override = default;
|
||||
@@ -155,7 +147,7 @@ class RomServiceImpl final : public proto::RomService::Service {
|
||||
|
||||
private:
|
||||
Config config_;
|
||||
Rom* rom_; // Not owned
|
||||
RomGetter rom_getter_;
|
||||
RomVersionManager* version_mgr_; // Not owned, may be null
|
||||
ProposalApprovalManager* approval_mgr_; // Not owned, may be null
|
||||
|
||||
|
||||
@@ -14,17 +14,19 @@
|
||||
#include "app/service/imgui_test_harness_service.h"
|
||||
#include "protos/canvas_automation.grpc.pb.h"
|
||||
|
||||
#include "app/editor/editor_manager.h"
|
||||
|
||||
namespace yaze {
|
||||
|
||||
YazeGRPCServer::YazeGRPCServer() : is_running_(false) {}
|
||||
|
||||
// Destructor defined here so CanvasAutomationServiceGrpc is a complete type
|
||||
YazeGRPCServer::~YazeGRPCServer() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
absl::Status YazeGRPCServer::Initialize(
|
||||
int port, test::TestManager* test_manager, Rom* rom,
|
||||
int port, test::TestManager* test_manager,
|
||||
RomGetter rom_getter,
|
||||
net::RomVersionManager* version_mgr,
|
||||
net::ProposalApprovalManager* approval_mgr,
|
||||
CanvasAutomationServiceImpl* canvas_service) {
|
||||
@@ -39,14 +41,12 @@ absl::Status YazeGRPCServer::Initialize(
|
||||
test_harness_service_ =
|
||||
std::make_unique<test::ImGuiTestHarnessServiceImpl>(test_manager);
|
||||
std::cout << "✓ ImGuiTestHarness service initialized\n";
|
||||
} else if (config_.enable_test_harness) {
|
||||
std::cout << "⚠ ImGuiTestHarness requested but no TestManager provided\n";
|
||||
}
|
||||
|
||||
// Create ROM service if rom provided
|
||||
if (config_.enable_rom_service && rom) {
|
||||
// Create ROM service if rom_getter provided
|
||||
if (config_.enable_rom_service && rom_getter) {
|
||||
rom_service_ =
|
||||
std::make_unique<net::RomServiceImpl>(rom, version_mgr, approval_mgr);
|
||||
std::make_unique<net::RomServiceImpl>(rom_getter, version_mgr, approval_mgr);
|
||||
|
||||
// Configure ROM service
|
||||
net::RomServiceImpl::Config rom_config;
|
||||
@@ -115,6 +115,19 @@ absl::Status YazeGRPCServer::StartAsync() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status YazeGRPCServer::AddService(
|
||||
std::unique_ptr<grpc::Service> service) {
|
||||
if (!service) {
|
||||
return absl::InvalidArgumentError("Service is null");
|
||||
}
|
||||
if (is_running_) {
|
||||
return absl::FailedPreconditionError(
|
||||
"Cannot add services after the server has started");
|
||||
}
|
||||
extra_services_.push_back(std::move(service));
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void YazeGRPCServer::Shutdown() {
|
||||
if (server_ && is_running_) {
|
||||
std::cout << "⏹ Shutting down unified gRPC server...\n";
|
||||
@@ -163,6 +176,10 @@ absl::Status YazeGRPCServer::BuildServer() {
|
||||
builder.RegisterService(canvas_grpc_service_.get());
|
||||
}
|
||||
|
||||
for (auto& service : extra_services_) {
|
||||
builder.RegisterService(service.get());
|
||||
}
|
||||
|
||||
// Build and start
|
||||
server_ = builder.BuildAndStart();
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/net/rom_version_manager.h"
|
||||
@@ -19,8 +21,12 @@ namespace yaze {
|
||||
|
||||
// Forward declarations
|
||||
class CanvasAutomationServiceImpl;
|
||||
|
||||
class Rom;
|
||||
|
||||
namespace editor {
|
||||
class EditorManager;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class ProposalApprovalManager;
|
||||
class RomServiceImpl;
|
||||
@@ -34,31 +40,11 @@ class ImGuiTestHarnessServiceImpl;
|
||||
/**
|
||||
* @class YazeGRPCServer
|
||||
* @brief YAZE's unified gRPC server for Zelda3 editor automation
|
||||
*
|
||||
* This server combines multiple automation services for the Zelda editor:
|
||||
* 1. ImGuiTestHarness - GUI test automation (widget discovery, screenshots,
|
||||
* etc.)
|
||||
* 2. RomService - ROM manipulation (read/write, proposals, version management)
|
||||
* 3. CanvasAutomation - Canvas operations (tiles, selection, zoom, pan)
|
||||
*
|
||||
* All services share the same gRPC server instance and port, allowing
|
||||
* clients (CLI, AI agents, remote scripts) to interact with GUI, ROM data,
|
||||
* and canvas operations simultaneously.
|
||||
*
|
||||
* Example usage:
|
||||
* ```cpp
|
||||
* YazeGRPCServer server;
|
||||
* server.Initialize(50051, test_manager, rom, version_mgr, approval_mgr,
|
||||
* canvas_service); server.Start();
|
||||
* // ... do work ...
|
||||
* server.Shutdown();
|
||||
* ```
|
||||
*/
|
||||
class YazeGRPCServer {
|
||||
public:
|
||||
/**
|
||||
* @brief Configuration for the unified server
|
||||
*/
|
||||
using RomGetter = std::function<Rom*()>;
|
||||
|
||||
struct Config {
|
||||
int port = 50051;
|
||||
bool enable_test_harness = true;
|
||||
@@ -68,56 +54,31 @@ class YazeGRPCServer {
|
||||
};
|
||||
|
||||
YazeGRPCServer();
|
||||
// Destructor must be defined in .cc file to allow deletion of incomplete
|
||||
// types
|
||||
~YazeGRPCServer();
|
||||
|
||||
/**
|
||||
* @brief Initialize the server with all required services
|
||||
* @param port Port to listen on (default 50051)
|
||||
* @param test_manager TestManager for GUI automation (optional)
|
||||
* @param rom ROM instance for ROM service (optional)
|
||||
* @param version_mgr Version manager for ROM snapshots (optional)
|
||||
* @param approval_mgr Approval manager for proposals (optional)
|
||||
* @param canvas_service Canvas automation service implementation (optional)
|
||||
* @param port Port to listen on
|
||||
* @param test_manager TestManager for GUI automation
|
||||
* @param rom_getter Function to retrieve active ROM
|
||||
* @param version_mgr Version manager for ROM snapshots
|
||||
* @param approval_mgr Approval manager for proposals
|
||||
* @param canvas_service Canvas automation service implementation
|
||||
* @return OK status if initialized successfully
|
||||
*/
|
||||
absl::Status Initialize(
|
||||
int port, test::TestManager* test_manager = nullptr, Rom* rom = nullptr,
|
||||
int port, test::TestManager* test_manager = nullptr,
|
||||
RomGetter rom_getter = nullptr,
|
||||
net::RomVersionManager* version_mgr = nullptr,
|
||||
net::ProposalApprovalManager* approval_mgr = nullptr,
|
||||
CanvasAutomationServiceImpl* canvas_service = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Start the gRPC server (blocking)
|
||||
* Starts the server and blocks until Shutdown() is called
|
||||
*/
|
||||
absl::Status Start();
|
||||
|
||||
/**
|
||||
* @brief Start the server in a background thread (non-blocking)
|
||||
* Returns immediately after starting the server
|
||||
*/
|
||||
absl::Status StartAsync();
|
||||
|
||||
/**
|
||||
* @brief Shutdown the server gracefully
|
||||
*/
|
||||
absl::Status AddService(std::unique_ptr<grpc::Service> service);
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* @brief Check if server is currently running
|
||||
*/
|
||||
bool IsRunning() const;
|
||||
|
||||
/**
|
||||
* @brief Get the port the server is listening on
|
||||
*/
|
||||
int Port() const { return config_.port; }
|
||||
|
||||
/**
|
||||
* @brief Update configuration (must be called before Start)
|
||||
*/
|
||||
void SetConfig(const Config& config) { config_ = config; }
|
||||
|
||||
private:
|
||||
@@ -126,12 +87,11 @@ class YazeGRPCServer {
|
||||
std::unique_ptr<test::ImGuiTestHarnessServiceImpl> test_harness_service_;
|
||||
std::unique_ptr<net::RomServiceImpl> rom_service_;
|
||||
CanvasAutomationServiceImpl* canvas_service_ = nullptr;
|
||||
// Store as base grpc::Service* to avoid incomplete type issues
|
||||
std::unique_ptr<grpc::Service> canvas_grpc_service_;
|
||||
std::unique_ptr<grpc::Service> test_harness_grpc_wrapper_;
|
||||
std::vector<std::unique_ptr<grpc::Service>> extra_services_;
|
||||
bool is_running_;
|
||||
|
||||
// Build the gRPC server with all services
|
||||
absl::Status BuildServer();
|
||||
};
|
||||
|
||||
@@ -139,10 +99,3 @@ class YazeGRPCServer {
|
||||
|
||||
#endif // YAZE_WITH_GRPC
|
||||
#endif // YAZE_APP_CORE_SERVICE_UNIFIED_GRPC_SERVER_H_
|
||||
|
||||
// Backwards compatibility alias
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
namespace yaze {
|
||||
using UnifiedGRPCServer = YazeGRPCServer;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user