Skip to content

Commit

Permalink
Core: Workflow to generate RPC headers
Browse files Browse the repository at this point in the history
  • Loading branch information
zach2good committed Aug 27, 2024
1 parent 3166a0b commit c834ccd
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 1 deletion.
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")

0 comments on commit c834ccd

Please sign in to comment.