Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Workflow to generate RPC headers #6179

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,33 @@ endif()
add_library(project_options INTERFACE)
target_compile_features(project_options INTERFACE cxx_std_17)

# Find Python (defines ${Python_EXECUTABLE})
find_package(Python REQUIRED)
message(STATUS "Python_EXECUTABLE: ${Python_EXECUTABLE}")
message(STATUS "Python_VERSION: ${Python_VERSION}")
if(NOT ${Python_VERSION_MAJOR} EQUAL 3)
message(FATAL_ERROR "Python 3 is required")
endif()

# Generate RPC calls from IDL
set_property(
DIRECTORY
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tools/generate_rpc_stubs.py
)
message(STATUS "Generating RPC stubs")
message(STATUS "Calling: ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/generate_rpc_stubs.py ${CMAKE_BINARY_DIR}")
execute_process(
COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/generate_rpc_stubs.py ${CMAKE_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE RPC_EXIT_CODE
)
if (NOT RPC_EXIT_CODE EQUAL 0)
message(FATAL_ERROR "Failed to generate RPC stubs")
endif()

include_directories(${CMAKE_BINARY_DIR}/generated) # Globally include the build/generated directory

# Link this 'library' to use the warnings specified in CompilerWarnings.cmake
add_library(project_warnings INTERFACE)
set_project_warnings(project_warnings)
Expand Down
9 changes: 9 additions & 0 deletions src/world/world_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,12 @@ void WorldServer::Tick()
{
Application::Tick();
}

auto WorldServer::playersOnline_REQ_RECV(TestStruct) -> std::size_t
{
return 0;
}

void WorldServer::playersOnline_RES_SEND(std::size_t)
{
}
7 changes: 6 additions & 1 deletion src/world/world_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,20 @@
#include "conquest_system.h"
#include "http_server.h"
#include "message_server.h"
#include "rpc_world_service.h"

class WorldServer final : public Application
class WorldServer final : public Application, public IRPCWorldService
{
public:
WorldServer(int argc, char** argv);
~WorldServer() override;

void Tick() override;

// IRPCWorldService
auto playersOnline_REQ_RECV(TestStruct) -> std::size_t override;
void playersOnline_RES_SEND(std::size_t) override;

std::unique_ptr<HTTPServer> httpServer;
std::unique_ptr<message_server_wrapper_t> messageServer;

Expand Down
123 changes: 123 additions & 0 deletions tools/generate_rpc_stubs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Assumed to be run by CMake from the root of the repository.
# Generated files are placed in /build/generated/
# Usage: python tools/generate_rpc_stubs.py "${CMAKE_BINARY_DIR}"

import os
import sys

# ========================
# Define RPC transport structs here
#
# Don't use dynamic arrays or pointers in these structs.
# ========================

rpc_structs = [
("TestStruct", "int x", "int y", "float z"),
("TestStruct2", "int x", "int y", "float z"),
]

# ========================
# Define RPC calls here
#
# Each of these generates up to 4 functions:
# 1. Send function in origin service (SEND)
# 2. Receive function in destination service (RECV)
# 3. Send function in destination service (SEND)
# 4. Receive function in origin service (RECV)
#
# So each service will end up with up to 2 function calls per RPC call.
#
# Format:
# (
# (Origin Service, Destination Service),
# Request Name,
# Response Arg (string of C++ type),
# Request Args (List of strings of C++ types, can be an empty list),
# )
#
# C++ types can be numerics, structs, enums, etc. (anything that can be serialized).
# ========================

rpc_calls = [
# Map to World
(("map", "world"), "playersOnline", "std::size_t", ["TestStruct"]),
]

# ========================

# Get CMAKE_BINARY_DIR from args
if len(sys.argv) < 2:
print('Usage: python tools/generate_rpc_stubs.py "${CMAKE_BINARY_DIR}"')
sys.exit(1)

CMAKE_BINARY_DIR = sys.argv[1]
GENERATED_PATH = f"{CMAKE_BINARY_DIR}/generated"

print(f"Generating RPC stubs in {GENERATED_PATH}")

# Ensure the generated directory exists
if not os.path.exists(f"{CMAKE_BINARY_DIR}/generated"):
os.makedirs(f"{CMAKE_BINARY_DIR}/generated")

AUTO_GENERATED_WARNING = """// THIS FILE IS AUTO-GENERATED BY `tools/generate_rpc_stubs.py`
// ANY CHANGES MADE HERE WILL BE OVERWRITTEN BY THE GENERATOR\n"""

map_rpc_funcs = []
world_rpc_funcs = []

for rpc_call in rpc_calls:
origin = rpc_call[0][0]
dest = rpc_call[0][1]
func_name = rpc_call[1]
response_arg = rpc_call[2]
request_args = rpc_call[3]

if origin == "map":
# 1. Send function in origin service (SEND)
map_rpc_funcs.append(
f" virtual void {func_name}_REQ_SEND({', '.join(request_args)}) = 0;"
)
# 4. Receive function in origin service (RECV)
map_rpc_funcs.append(
f" virtual auto {func_name}_RES_RECV() -> {response_arg} = 0;"
)

if dest == "world":
# 2. Receive function in destination service (RECV)
world_rpc_funcs.append(
f" virtual auto {func_name}_REQ_RECV({', '.join(request_args)}) -> {response_arg} = 0;"
)
# 3. Send function in destination service (SEND)
world_rpc_funcs.append(
f" virtual void {func_name}_RES_SEND({response_arg}) = 0;"
)

with open(f"{CMAKE_BINARY_DIR}/generated/rpc_structs.h", "w") as f:
f.write(AUTO_GENERATED_WARNING)
f.write("#pragma once\n\n")
f.write("#include <cstddef>\n")
for struct in rpc_structs:
f.write(f"\nstruct {struct[0]}\n{{\n")
for member in struct[1:]:
f.write(f" {member};\n")
f.write("};\n")

with open(f"{CMAKE_BINARY_DIR}/generated/rpc_map_service.h", "w") as f:
f.write(AUTO_GENERATED_WARNING)
f.write("#pragma once\n\n")
f.write('#include "rpc_structs.h"\n\n')
f.write("#include <cstddef>\n\n")
f.write("class IRPCMapService\n{\npublic:\n")
f.write(" virtual ~IRPCMapService() = default;\n\n")
f.write("\n".join(map_rpc_funcs))
f.write("\n};\n")

with open(f"{CMAKE_BINARY_DIR}/generated/rpc_world_service.h", "w") as f:
f.write(AUTO_GENERATED_WARNING)
f.write("#pragma once\n\n")
f.write('#include "rpc_structs.h"\n\n')
f.write("#include <cstddef>\n\n")
f.write("class IRPCWorldService\n{\npublic:\n")
f.write(" virtual ~IRPCWorldService() = default;\n\n")
f.write("\n".join(world_rpc_funcs))
f.write("\n};\n")
Loading