最近有项目需要在UE5里做RPC,对比了thrift、gRPC、rcplib等开源rpc框架,由于习惯使用protobuf,故选择了gRPC。然而,Google出品也是一言难尽啊,最起码编译太繁琐了。
本次使用的gRPC版本为1.62.1,UE5.2,以插件的形式集成,支持DebugGame/DebugGame Editor/Development/Development Editor/Shipping(目前只测试这几个)。
插件免费,下载地址:【免费】UE5gRPC1.62.1插件资源-CSDN文库
测试案例下载地址:【免费】UE5gRPC插件测试案例资源-CSDN文库
0、目的
本来已经有人在git上做了比较好的插件,但是,我们搞C++的,总想着都是开源的东西,最好自己也来封装一个可以把控的东东,用起来放心,有什么功能需要扩展也可以自己接着肝。
了解一下如何一步一步封装起来的过程也是值得期待的事情,在能力范围内,最大的自由度总是带来无限的快感 ^_^
1、所谓坑位
- gRPC依赖的库zlib与boringssl-with-bazel(boringssl-with-bazel包括crypto、ssl,这2人起主要冲突)与UE5相关库的名字重定义冲突。
- gRPC编译动态库极其困难,1.62.1版本编译动态库会出现upb相关的符号丢失问题。
2、尝试解决
- UE5的编译器选项AdditionalLinkerOptions,本来想模拟C++链接器的“/FORCE:MUTIPLE”,结果不起作用;
- 将gRPC换成动态库,想着UE里的冲突库是静态库,可能动态加载Grpc可行,但是动态库就需要显示加载,很麻烦,而且gRPC编译动态库upb库符号导出会出问题。最终,将crypto、ssl以及zlib换成动态库尝试(毕竟就这几个报错集成不到UE里),结果动态库的导出文件.lib与对应的静态库.lib还是冲突。【后来反思了一下,这种想法是博主编译链接知识太菜了】
- 初始想法是哪个库冲突就唯一化哪个库。一个办法就是将UE的库升级到gRPC官方推荐的boringssl-with-bazel;另一个方法就是将Grpc依赖的库zlib与boringssl-with-bazel换成UE引擎的ThirdParty的同名库。
最终,最后一种想法的最后一种方法尝试成功。
3、Cmake配置
CMAKE组里:CMAKE_INSTALL_PREFIX D:/grpc
ABSL里的CXX_STD勾选上
配置UE5的OPENSSL+ZLIB
4、库编译
选择Release,右键ALL_BUILD生成(采用VS2019)
右键INSTALL生成
生成的gRPC库文件
5、插件目录结构
其中,libgrpc1.62.1是grpc库,LibGrpc是插件源码目录。
其中,helloworld.grpc.pb.cc和helloworld.grpc.pb.h,helloworld.pb.cc和helloworld.pb.h是proto文件生成的。
cmd里的指令如下,输出目录改成自定义的,其中用到的exe程序均在生成的库目录的 grpc/bin 下。不同语言之间要用到的RPC工具博主也生成了,都在bin里面,对应的指令大家自己百度一下即可,由于博主的客户端也是Cpp的,就共用了。
protoc.exe --grpc_out=输出目录 --plugin=protoc-gen-grpc=D:\grpc\bin\grpc_cpp_plugin.exe helloworld.proto
protoc.exe --cpp_out=输出目录helloworld.proto
6、LibGrpc.Build.cs
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.IO;
public class LibGrpc : ModuleRules
{
public LibGrpc(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
bEnableUndefinedIdentifierWarnings = false;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
//set grpc library for Windows
string GrpcPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../libgrpc1.62.1/"));
//include the grpc header files
string IncludePath = GrpcPath + "include/";
PublicSystemIncludePaths.AddRange(new string[] { IncludePath });
//use engine library
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenSSL");
AddEngineThirdPartyPrivateStaticDependencies(Target, "zlib");
if (Target.Platform == UnrealTargetPlatform.Win64)
{
//add the grpc libs
string LibPath = GrpcPath + "lib/Win64/";
foreach (string file in Directory.GetFiles(LibPath))
{
PublicAdditionalLibraries.Add(file);
}
}
else if (Target.Platform == UnrealTargetPlatform.Linux){
System.Console.WriteLine("Not currently supported on Linux");
}
else // unsupported platform
{
System.Console.WriteLine("Only supported on Windows");
}
}
}
7、插件测试
采用Grpc自带的示例测试,helloworld.proto。
helloworld.grpc.pb.cc、helloworld.grpc.pb.h、helloworld.pb.cc和helloworld.pb.h是协议自生成的文档,greeter_server.cc可以封装到UE的内部组织里,比如将其逻辑写在FRunnable的子类里,然后暴露给BlueprintFunctionLibrary让蓝图调用。
使用之前需要测试全面,至少包括:DebugGame/DebugGame Editor/Development/Development Editor/Shipping,测试一下插件编译是否成功。
至此,UE5的gRPC插件封装完成,祝大家使用愉快,编译一次丝滑通过。
有问题随时联系我,共同踩坑,共同进步!