There was some unsolved issues in my old Medium article, dozens of people viewed it every day and tried to use it nevetheless, so I deleted it. Now there are no issues, and it’s possible to link and flawlessly use protobuf without restrictions

Before we start, I suggest you to reconsider using grpc - you can use nice built-in Unreal Engine’s automatic serialization, replication, and RPCs with USTRUCTs. Even though UPROPERTIES have blunt capabilities - nested arrays and maps are not allowed (?), you can’t have recursive structures (??), and there are no basic things such as variants and optionals (???), it will be much more efficient to use TArrays, TMaps, and FStrings directly. You will need to convert containers and strings anyway to pass them to the engine - it’s better to store and send them directly.

But sometimes this is not the case, and you have to use it for accessing some other services, or you need the standard library, or something else. In this case, you might stumble into an insane amount of problems, such as link conflicts, dynamic loading issues, macro redefinitions. Luckily, this is all solvable.

Just sharing an example project won’t work because protobuf-generated files should be built with the same version of protobuf you’re linking with, and linked libraries depend on specific windows libraries, and you have to do some specific steps every time you generate proto files. One could probably automate that someday.

Install GRPC Correctly

Link statically - it works in newer versions. Don’t ever try to link protobuf dynamically - it’s badly designed and you will get many unsolvable issues this way. My protobuf version is 3.19.4, but try the latest version at first.

For Windows, I used vcpkg to install this specific triplet:

./vcpkg.exe install grpc:x64-windows-static-md

Then you have to link grpc and all dependencies (protobuf, zlib, openssl, c-ares, and possibly some more) with your Unreal Engine project. For simplicity, I just merged all include and lib directories from vcpkg’s installed directory.

cp installed/<library>x64-windows-static-md/lib/* ../MyProject/ThirdParty/lib

Put this into MyProject.Build.cs

PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "include"));

string LibrariesPath = Path.Combine(ThirdPartyPath, "lib");
DirectoryInfo d = new DirectoryInfo(LibrariesPath);
FileInfo[] Files = d.GetFiles("*.lib");
foreach (FileInfo file in Files)
{
    PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, file.Name)); 
}

Definitions.Add(string.Format( "WITH_GRPC_BINDING=1"));

Illegal Library Movements

Then we’ll have to make a couple of weird steps.

First, if you have libcurl (or any other library that makes use of SSL) linked to your Unreal Engine project, you will get conflicts with openssl in your new lib dir. To resolve them you can delete these from ThirdParty/lib dir:

rm libcrypto.lib libssl.lib

If you’re actually going to use SSL channels in grpc, then it’s better to make sure that openssl versions are matching (and that’s a different story), or it’s undefined behaviour.

Second weird step: libraries depend on these libraries from Windows distribution: crypt32.lib iphlpapi.lib userenv.lib psapi.lib ws2_32.lib. Find them on your C drive (they can be in Windows SDK, for example) and either link against them in Strategy.Build.cs or just copy them into lib directory.

Compile the project to see if it works!

Usage

Your .proto files should contain this code:

option optimize_for = LITE_RUNTIME;
option cc_enable_arenas = true;

Then you just usually compile your protos with protoc from vcpkg’s installed directory.

"installed/tools/protobuf/protoc.exe" -I . --grpc_out=cpp --plugin=protoc-gen-grpc="/full/path/to/installed/tools/grpc/grpc_cpp_plugin.exe" cc/MyProject/*

"installed/tools/protobuf/protoc.exe" -I . --cpp_out=cpp  cc/MyProject/*

For every protoc invocation

Then you have to edit every .cc file generated (not the headers) and add some code before first include and some code after first include.

This goes before the first include:

#include "EnableGrpcIncludes.h"

This goes after the last include:

#include "DisableGrpcIncludes.h"

I will give the contents of these files at the end of the article: add them to your project.

For every file in your project

If you include any generated headers or just protobuf/grpc headers, you’ll have to wrap them with #include "EnableGrpcIncludes.h" and #include "DisableGrpcIncludes.h" too.

EnableGrpcIncludes.h

#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
#pragma warning (disable : 4005) // macro redefinition
#pragma warning (disable : 4005) // initializers put in library initialization area
#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

DisableGrpcIncludes.h

#ifndef __ANDROID__
#include "Windows/HideWindowsPlatformTypes.h"
#endif

(if you want, you can re-enable warnings that were disabled in the first file)

Conclusion

That’s it, now it should work flawlessly: both serving and making requests. If you have any issues, message me.

Probably some libraries from lib can be deleted too, because not everything from there is a dependency.