GRPC with UE4 on Windows

Vsevolod Kvachev
Oct 3, 2019 · 3 min read

I’ve been struggling with getting grpc to work with UE4 and now I’ve got it running. It had a lot of problems with windows headers. Also, protobuf authors didn’t bother to implement correct unloading, so hot reloading stops working UNLESS you use protobuf-lite. Also, we are not going to enable RTTI.

I only implemented linkage for Windows. I will add Linux support sometime later.

Let’s begin.

1. build grpc with -DgRPC_USE_PROTO_LITE=ON flag.

You can use nice tool vcpkg for building it with really configuration for Windows. Go to ports/grpc/portfile.cmake and put required -DgRPC_USE_PROTO_LITE=ON flag among with other flags for cmake

vcpkg install grpc:x64-windows

Now go drink some tea.

2. After it’s done, go to vcpkg/packages and notice c-ares, openssl, zlib, protobuf and grpc there.

I just vendored them into ./ThirdParty directory of UE4 project //TODO: find a better place for them . We can clean unneeded stuff from each once, but leave include and lib directories.

3. Open your ./Source/Project.build.cs file and merge next code into it:

using System.IO;
using UnrealBuildTool;
public class Strategy: ModuleRules {
private string ModulePath {
get {
return ModuleDirectory;
}
}
private string ThirdPartyPath {
get {
return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/"));
}
}
public Strategy(ReadOnlyTargetRules Target): base(Target) {
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] {
"Core",
"CoreUObject",
"Engine",
"InputCore",
"NavigationSystem",
"AIModule",
"ProceduralMeshComponent"
});
LoadThirdPartyLibrary("openssl-windows", Target);
LoadThirdPartyLibrary("zlib", Target);
LoadThirdPartyLibrary("c-ares", Target);
LoadThirdPartyLibrary("protobuf-lite", Target);
LoadThirdPartyLibrary("grpc", Target);
Definitions.Add(string.Format("WITH_GRPC_BINDING=1"));
}
public void LoadThirdPartyLibrary(string libraryName, ReadOnlyTargetRules Target, bool dynamic = false) {
bool isLibrarySupported = false;
if (Target.Platform == UnrealTargetPlatform.Win64) {
isLibrarySupported = true;
string PlatformString = "x64";
string libraryDir = libraryName + "_" + PlatformString + "-windows";
if (!dynamic) {
string LibrariesPath = Path.Combine(ThirdPartyPath, libraryDir, "lib");
DirectoryInfo d = new DirectoryInfo(LibrariesPath);
FileInfo[] Files = d.GetFiles("*.lib");
foreach(FileInfo file in Files) {
PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, file.Name));
}
}
PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, libraryDir, "include"));
}
}
}

4. Rename ./ThirdParty/protobuf_x64-windows to ./ThirdParty/protobuf-lite_x64-windows . Remove ./ThirdParty/protobuf-lite_x64/lib/libprotobuf.lib , we need only second file.

5. Build your protos with protoc and cpp plugin from newly built grpc and protobuf. Note that you should have this line present in your proto files:

option optimize_for = LITE_RUNTIME;

5. Now you should put this abomination into beginning of every *.pb.cc and *.grpc.pb.cc file you’re going to build inside your project.

#define GRPC_USE_PROTO_LITE 1
static void MemoryBarrier() {}
#pragma warning (disable : 4800) // forcing value to bool true or false
#pragma warning (disable : 4125) // decimal digit terminates octal escape sequence
#pragma warning (disable : 4647) // behavior change __is_pod has different value in previous version
#pragma warning (disable : 4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
#pragma warning (disable : 4582) // constructor is not implicitly called
#pragma warning (disable : 4583) // destructor is not implicitly called
#pragma warning (disable : 4946) // reinterpret_cast
#define GOOGLE_PROTOBUF_NO_RTTI true
#include "AllowWindowsPlatformTypes.h"
#pragma intrinsic(_InterlockedCompareExchange64)
#define InterlockedCompareExchangeAcquire64 _InterlockedCompareExchange64
#define InterlockedCompareExchangeRelease64 _InterlockedCompareExchange64
#define InterlockedCompareExchangeNoFence64 _InterlockedCompareExchange64
#define InterlockedCompareExchange64 _InterlockedCompareExchange64

6. Carefully put next code in every .pb.cc and .grpc.pb.cc after last include.

#include "HideWindowsPlatformTypes.h"

7. WARNING! You will have wrap protobuf and grpc includes with these code pieces above too.

8. That’s it! Here is example client for your convenience:

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using namespace mygame;
class GameClient {
public:
GameClient(std::shared_ptr<Channel> channel): stub_(Game::NewStub(channel)) {}
void GetMap() {
GetMapRequest request;
request.set_intvalue(321);
GetMapResponse reply;ClientContext context;Status status;
status = stub_->GetMap(&context, request, &reply);
UE_LOG(LogTemp, Warning, TEXT("status"));
if (status.ok()) {
UE_LOG(LogTemp, Warning, TEXT("Ok"));
int x = int(reply.map().height());
UE_LOG(LogTemp, Warning, TEXT("%d"), x);
} else {
UE_LOG(LogTemp, Warning, TEXT("Not ok"));
UE_LOG(LogTemp, Warning, TEXT("%s"), * FString(std::string(status.error_message()).c_str()));
}
}
private:
std::unique_ptr<Game::Stub> stub_;
};

Ask me if it still doesn’t work.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade