I’ve been struggling with getting grpc to work with UE4 and now I’ve got it running. There were a lot of issues, for example protobuf authors didn’t bother to implement correct unloading, so hot reloading stops working UNLESS you use protobuf-lite.

I only implemented linkage for Windows. I will add Linux and Android 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 fast 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 you can 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 my UE4 project. We can clean unneeded stuff from each one, 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. Put these lines in your proto files

option optimize_for = LITE_RUNTIME;
option cc_enable_arenas = true;

6. Build your protos with protoc and cpp plugin from newly built grpc and protobuf

"/path/to/protobuf-lite_x64-windows/tools/protobuf/protoc.exe" -I . --grpc_out=cpp --plugin=protoc-gen-grpc="/path/to/ThirdParty/grpc_x64-windows/tools/grpc/grpc_cpp_plugin.exe" myprotos/*

"/path/to/protobuf-lite_x64-windows/tools/protobuf/protoc.exe" -I . --cpp_out=cpp myprotos/*

7. 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
#ifndef WORKAROUND_SYMBOL_MEMORY_BARRIER
#define WORKAROUND_SYMBOL_MEMORY_BARRIER
static void MemoryBarrier() {}
#endif
#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
#ifndef __ANDROID__
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
#pragma intrinsic(_InterlockedCompareExchange64)
#define InterlockedCompareExchangeAcquire64 _InterlockedCompareExchange64
#define InterlockedCompareExchangeRelease64 _InterlockedCompareExchange64
#define InterlockedCompareExchangeNoFence64 _InterlockedCompareExchange64
#define InterlockedCompareExchange64 _InterlockedCompareExchange64

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

#include "HideWindowsPlatformTypes.h"

9. WARNING! In your UE4 project sources, you will have to wrap all includes of protoc-generated code too.

10. WARNING! Calling destructors of generated types causes crashes. Luckily, there is protobuf feature called Arenas. They give you ability no release memory without calling destructors. Use it.

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

GetWorldResponse* GameClient::GetWorld(GetWorldRequest* request) 
{
auto reply = google::protobuf::Arena::CreateMessage<GetWorldResponse>(Arena);

ClientContext context;

Status status;
status = stub_->GetWorld(&context, *request, reply);
UE_LOG(LogTemp, Warning, TEXT("status"));

if (status.ok()) {
UE_LOG(LogTemp, Warning, TEXT("GetWorld OK"));
} else {
UE_LOG(LogTemp, Warning, TEXT("GetWorld Failed"));
UE_LOG(LogTemp, Warning, TEXT("%s"), *FString(std::string(status.error_message()).c_str()));
return nullptr;
}
return reply;
}

After completing all these steps, it works for me. Please contact me if it still doesn’t work for you.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store