Skip to main content

RPC and Code Generation

Quick Start​

For RPC, protocol, Excel, or database changes, select a task in the development quick start. Existing declarations drive generation; routine extensions need no new templates. Use the design sections below for generation changes.

Customization and Design​

Protocol as the Source of Truth​

Business RPC and data protocols use protobuf. Shared framework protocols live in src/server_frame/protocol/; services and components have their own protocol/ directories. The atgateway client wire protocol uses FlatBuffers.

protocol/
├── private/protocol/ # Server-internal only
│ ├── pbdesc/ # svr.protocol.proto (RouterService/LogicCommonService), svr.*.table.proto (DB tables)
│ ├── common/ config/ extension/ log/
└── public/protocol/ # Shareable with clients
├── extension/atframework.proto # Custom service/rpc options (drive code generation)
└── pbdesc/ com.protocol*.proto (CS/SS messages), com.struct*.proto (data structures)

All generated artifacts are regenerated, never edited by hand (*.atfw.gen.{h,cpp}, *.pb.{h,cc}, config/db code).

Custom Options​

The extension options that drive code generation are spread across three files:

  • public/protocol/extension/atframework.proto: atframework.service_options (module_name, etc.) and atframework.rpc_options (api_name, allow_no_wait, etc.);
  • protocol/extension/xrescode_extensions_v3.proto: the xrescode.loader option, marking Excel config loaders;
  • private/protocol/extension/svr.database.extension.proto: DB table/index extensions (KV/KL/CAS/TTL), used by the db templates.

Generation Pipeline​

  • src/server_frame/generate_proto_source.cmake / generate_proto_utility.cmake: protoc compilation;
  • src/tools/generate-for-pb/mako-generator.py: reads the pb descriptor set and renders in bulk according to the rules declared in each CMakeLists (service name + template + output path);
  • src/tools/generate_for_pb_utility.cmake: CMake-side wrapper for invoking templates.

Template Inventory (src/templates/)​

TemplateGenerated artifact
handle_ss_rpc.*.makoSS RPC registration function register_handles_for_<service> (generated under the app/ entry-point directory; the service declaration routes it via MAIN_DIRECTORIES into MAIN_SOURCES/MAIN_HEADERS, compiled into the service executable, not the private static library)
task_action_ss_rpc.*.makoServer-side task action skeleton for each SS RPC method
rpc_call_api_for_ss.*.makoSS RPC client call APIs (unary/stream/no-wait/broadcast/metadata/user/router variants) and per-RPC full-name accessors
handle_cs_rpc.*.mako / task_action_cs_rpc.*.makoHandlers and task actions for client RPCs
session_downstream_api_for_cs.*.makoServer→client session downstream push APIs and per-RPC full-name accessors
package_request_api_for_simulator.*.makoCS request packing APIs for the robot/simulator
task_action_no_msg.*.makoNo-message task actions (used with src/generate-nomsg-task.sh)
db_interface.*.mako / db_rpc_redis(.kv/.kl).*.makoRedis database access layer (rpc/db/local_db_interface.atfw.gen.*)
config_manager.*.mako / config_set.*.mako / config_easy_api.*.makoExcel config loading framework and convenient read APIs

The orbit component has its own dedicated templates: src/component/orbit/sdk/server/template/ (they also generate per-RPC full-name accessors).

The SS template generates gsl::string_view get_full_name_of_<rpc>() inside the packer sub-namespace (alongside pack_<rpc>/unpack_<rpc>, declared in <service>.atfw.gen.h), returning the on-wire full RPC name (package.Service/method). The CS template generates it in the same packer sub-namespace. The orbit fork also uses the packer sub-namespace, returning its dotted protocol name. When registering SS mocks or asserting call history via test.ss(), use the SS/CS-template accessor instead of a hardcoded string (see RPC unit testing); the orbit-fork getter returns its dotted name and orbit RPCs traverse orbit transport (not the SS engine), so it must not be passed to test.ss().

RPC Caller API Shape​

RPCs requiring a response await completion and obtain results through RPC_AWAIT_CODE_RESULT or RPC_AWAIT_TYPE_RESULT. Even when the response or return value is unused, use RPC_AWAIT_IGNORE_RESULT to explicitly await and ignore it. These macros adapt C++20 and traditional stackful coroutines; see awaiting RPC completion.

For non-router RPCs, stream or allow_no_wait generates rpc::<module>::oneway::<rpc>(): it sends synchronously, can be called outside a task, returns the integer send result directly, and does not wait for a response. Ordinary unary calls can use __no_wait; __wait_later still sends a response-required request so the caller can wait later. broadcast is a separate fire-and-forget form.

SS methods with a streaming request or response, such as channel_event_sync, send one SSMsg per call rather than opening a persistent gRPC-style stream. Use generated headers for exact return types, target parameters, and namespaces.

Framework calls return integer error codes; rpc::result_code_type wraps results that require coroutine waits and is not an HTTP status type. System, atbus, and DB errors are defined in svr.const.err.proto; client business errors are defined in com.const.proto. Check business result fields in responses separately.