Repository: vizor-games/InfraworldRuntime
Branch: master
Commit: c3798cd62543
Files: 25
Total size: 112.5 KB
Directory structure:
gitextract_0btcdab7/
├── .gitignore
├── InfraworldRuntime.uplugin
├── LICENSE
├── README.md
├── Setup.bat
├── Setup.command
├── Setup.sh
└── Source/
└── InfraworldRuntime/
├── InfraWorldRuntime.Build.cs
├── Private/
│ ├── ChannelCredentials.cpp
│ ├── GrpcUriValidator.cpp
│ ├── InfraworldRuntime.cpp
│ ├── RpcClient.cpp
│ └── RpcClientWorker.cpp
└── Public/
├── CastUtils.h
├── ChannelCredentials.h
├── ChannelProvider.h
├── Conduit.h
├── GenUtils.h
├── GrpcIncludesBegin.h
├── GrpcIncludesEnd.h
├── GrpcUriValidator.h
├── InfraworldRuntime.h
├── RpcClient.h
├── RpcClientWorker.h
└── WorkerUtils.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Created by https://www.gitignore.io/api/linux,macos,windows
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.gitignore.io/api/linux,macos,windows
Binaries
GrpcIncludes
GrpcLibraries
GrpcPrograms
Intermediate
# grpc source codes
grpc
================================================
FILE: InfraworldRuntime.uplugin
================================================
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "Infraworld Runtime",
"Description": "Infraworld is a solution that enables Unreal Engine 4 to work with Google gRPC services from either C++ or Blueprints",
"Category": "Networking",
"CreatedBy": "Vizor Games LLC",
"CreatedByURL": "http://vizor-interactive.com/en/",
"DocsURL": "http://vizor-interactive.com/en/",
"MarketplaceURL": "",
"SupportURL": "mailto:roman.chehowski@vizor-games.com",
"CanContainContent": false,
"IsBetaVersion": false,
"Installed": false,
"Modules": [
{
"Name": "InfraworldRuntime",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
}
]
}
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Vizor Games LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
Vizor Infraworld
================
[](https://opensource.org/licenses/Apache-2.0)
[](https://codeclimate.com/github/vizor-games/InfraworldRuntime/maintainability)
Welcome to the Infraworld source code!

Infraworld is a solution that enables [Unreal Engine 4](https://www.unrealengine.com/en-US) to work with [Google gRPC](https://gRPC.io) services using either **C++** or **Blueprints**.
Infraworld is a fast, robust and cross platform.
It fits any stage of development: either prototyping or production. Saving a tons of your team's time, you need to write your gRPC wrappers by hand no more.
[A special converter utility](https://github.com/vizor-games/infraworld-cornerstone) will do it for you, producing high quality, debuggable and multi-threaded code, gaining lowest possible overhead to your game logic thread.
You may also work with either generated or shipped with gRPC C functions and C++ classes in your own way, even completely ignoring runtime classes, since the InfraworldRuntime adds all required headers and wires all required libraries.
Also, you may want to use a [protobuild](https://github.com/vizor-games/infraworld-protobuild) utility to automate cross-language gRPC wrapper generation.
Getting started
===============
##### Building gRPC support
At the first step, you need to build gRPC runtime libraries.
Just run `Setup.sh` for Linux, `Setup.bat` for Windows or `Setup.command` for macOS (please don't use `Setup.sh` on macOS, because Linux and macOS build pipelines are completely different!). OR you may want to use our sweet [pre-compiled binaries](../../releases) to avoid manual building and save our planet from carbon emission disaster! The runtime uses gRPC branch `v1.23.x`.
* For Windows, we recommend you to use [chocolatey](https://chocolatey.org) to install packages into your system.
**Note** that you do need all these programs in your system's `PATH` ([See how to edit PATH on Windows](https://www.computerhope.com/issues/ch000549.htm)):
* [Git VCS](https://git-scm.com/download/win)
* [Visual Studio 2017](https://visualstudio.microsoft.com/downloads/) with VC++ tools v141 installed
* [CMake](https://cmake.org) is used to generate a Visual Studio solution from the `CMakeLists.txt` provided with gRPC
* [Strawberry perl](http://strawberryperl.com) 64bit version
* [NASM](https://www.nasm.us)
* [Golang](https://golang.org/doc/install)
* For any distribution of Linux and for macOS systems you need (use `apt`, `pacman`, `emerge` or any other package manager to install this software):
* git
* automake, autoconf and libtool
* make
* strip
* go
* [Unreal Engine 4.22 installed](https://github.com/EpicGames/UnrealEngine/tree/4.22), additionally you need to `export UE_ROOT=/path/to/root/ue4/directory`, because you need UE4 to build GRPC for linux.
* For macOS (use `homebrew` or `macports` to install this software):
* git
* xCode 10.0+
* go
**Note** that required programs for Linux and MacOS systems are being checked in run-time.
Then you may (or may not) import `GrpcIncludes` and `GrpcLibraries` folders into your VCS, but you need to build them manually at least one time for each platform.
The build process requires an access to the Internet.
##### Installing the plugin
Just copy the resulting folder into the your project’s Plugins folder (create it if you don’t have one).
Then, after that project is being opened, a dialog box, telling that the plugin is need to be compiled should appear. Then confirm the dialog by clicking `Yes`.
##### Building and the converter
Please take a look at the [infraworld-cornerstone documentation](https://github.com/vizor-games/infraworld-cornerstone) for details.
##### Using generated code.
Please take a look at the [example project](https://drive.google.com/open?id=13EZzP_9033vBC7VzJf9LFrygg42LHaOW) for tutorial.
Running the example project
===========================
You should copy built plugin's folder into `InfraworldRuntimeExample/Plugins` folder.
Then just open `InfraworldDemo.uproject`. Server code is in `DemoServer` folder.
You are required to install dependencies using `pip` and `requirements.txt` file.
For more details please check our [**InfraworldExample**](https://github.com/vizor-games/InfraworldRuntimeExample) repository
Debugging
=========
Since the plugin itself is an open source software, you may want to debug it or add some extra functionality.
Since it is distributed as an Unreal Engine plugin, you can add it into your own game
and then generate **Visual Studio solution**, **XCode project** or **CMakeLists**. Use `Development` or `DebugGame` run configuration!
Contribution
============
Please feel free to report known bugs, propose new features and improve tests using Github's pull request system.
Please do not add either build libraries for an any platform or header files into your commits. Thank you very much for contributing into free software.
References
==========
* [Introduction to UE4 Plugins](https://wiki.unrealengine.com/An_Introduction_to_UE4_Plugins)
* [gRPC API docs](https://gRPC.io/docs/)
================================================
FILE: Setup.bat
================================================
@echo off
::#####################################VARS#############################################################################
set SCRIPT_FOLDER=%cd%
set GRPC_ROOT=%SCRIPT_FOLDER%\grpc
set GRPC_INCLUDE_DIR=%SCRIPT_FOLDER%\GrpcIncludes
set GRPC_LIBRARIES_DIR=%SCRIPT_FOLDER%\GrpcLibraries\Win64
set GRPC_PROGRAMS_DIR=%SCRIPT_FOLDER%\GrpcPrograms\Win64
set CMAKE_BUILD_DIR=%GRPC_ROOT%\.build
set REMOTE_ORIGIN=https://github.com/grpc/grpc.git
set BRANCH=v1.23.x
::#####################################VARS#############################################################################
:GET_UE_ROOT
IF "%UE_ROOT%" == "" (echo "UE_ROOT directory does not exist, please set correct UE_ROOT via SET UE_ROOT=<PATH_TO_UNREAL_ENGINE_FOLDER>" && GOTO ABORT)
:CLEAN
echo ">>>>>>>>>> clean"
IF EXIST "%GRPC_ROOT%" (cd "%GRPC_ROOT%" && git clean -fdx && git submodule foreach git clean -fdx && cd "%SCRIPT_FOLDER%")
IF EXIST "%GRPC_INCLUDE_DIR%" (rmdir "%GRPC_INCLUDE_DIR%" /s /q)
IF EXIST "%GRPC_LIBRARIES_DIR%" (rmdir "%GRPC_LIBRARIES_DIR%" /s /q)
IF EXIST "%GRPC_PROGRAMS_DIR%" (rmdir "%GRPC_PROGRAMS_DIR%" /s /q)
:CLONE_OR_PULL
echo ">>>>>>>>>> clone git"
if EXIST "%GRPC_ROOT%" (cd "%GRPC_ROOT%" && echo Pulling repo && git pull) else (call git clone "%REMOTE_ORIGIN%" && cd "%GRPC_ROOT%")
git fetch
git checkout -f
git checkout -t origin/%BRANCH%
git submodule update --init
:BUILD_ALL
mkdir "%CMAKE_BUILD_DIR%" && cd "%CMAKE_BUILD_DIR%"
call cmake .. -G "Visual Studio 16 2019" -A x64 ^
-DCMAKE_CXX_STANDARD_LIBRARIES="Crypt32.Lib User32.lib Advapi32.lib" ^
-DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_CONFIGURATION_TYPES=Release ^
-Dprotobuf_BUILD_TESTS=OFF ^
-DgRPC_ZLIB_PROVIDER=package ^
-DZLIB_INCLUDE_DIR="%UE_ROOT%\Engine\Source\ThirdParty\zlib\v1.2.8\include\Win64\VS2015" ^
-DZLIB_LIBRARY_DEBUG="%UE_ROOT%\Engine\Source\ThirdParty\zlib\v1.2.8\lib\Win64\VS2015\Debug\zlibstatic.lib" ^
-DZLIB_LIBRARY_RELEASE="%UE_ROOT%\Engine\Source\ThirdParty\zlib\v1.2.8\lib\Win64\VS2015\Release\zlibstatic.lib" ^
-DgRPC_SSL_PROVIDER=package ^
-DLIB_EAY_LIBRARY_DEBUG="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\Lib\Win64\VS2015\Debug\libcrypto.lib" ^
-DLIB_EAY_LIBRARY_RELEASE="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\Lib\Win64\VS2015\Release\libcrypto.lib" ^
-DLIB_EAY_DEBUG="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\Lib\Win64\VS2015\Debug\libcrypto.lib" ^
-DLIB_EAY_RELEASE="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\Lib\Win64\VS2015\Release\libcrypto.lib" ^
-DOPENSSL_INCLUDE_DIR="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\include\Win64\VS2015" ^
-DSSL_EAY_DEBUG="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\Lib\Win64\VS2015\Debug\libssl.lib" ^
-DSSL_EAY_LIBRARY_DEBUG="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\Lib\Win64\VS2015\Debug\libssl.lib" ^
-DSSL_EAY_LIBRARY_RELEASE="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\Lib\Win64\VS2015\Release\libssl.lib" ^
-DSSL_EAY_RELEASE="%UE_ROOT%\Engine\Source\ThirdParty\OpenSSL\1.1.1\Lib\Win64\VS2015\Release\libssl.lib"
call cmake --build . --target ALL_BUILD --config Release
:COPY_HEADERS
echo ">>>>>>>>>> copy headers"
robocopy "%GRPC_ROOT%\include" "%GRPC_INCLUDE_DIR%\include" /E
robocopy "%GRPC_ROOT%\third_party\protobuf\src" "%GRPC_INCLUDE_DIR%\third_party\protobuf\src" /E
:PATCH_HEADERS
echo ">>>>>>>>>> copy headers"
set GENERATED_MESSAGE_TABLE_DRIVEN_FILE="%SCRIPT_FOLDER%\third_party\protobuf\src\google\protobuf\"
:COPY_LIBRARIES
echo ">>>>>>>>>> copy libraries"
robocopy "%CMAKE_BUILD_DIR%\Release" "%GRPC_LIBRARIES_DIR%" *.lib /R:0 /S
robocopy "%CMAKE_BUILD_DIR%\third_party\cares\cares\lib\Release" "%GRPC_LIBRARIES_DIR%" *.lib /R:0 /S
robocopy "%CMAKE_BUILD_DIR%\third_party\benchmark\src\Release" "%GRPC_LIBRARIES_DIR%" *.lib /R:0 /S
robocopy "%CMAKE_BUILD_DIR%\third_party\gflags\Release" "%GRPC_LIBRARIES_DIR%" *.lib /R:0 /S
robocopy "%CMAKE_BUILD_DIR%\third_party\protobuf\Release" "%GRPC_LIBRARIES_DIR%" *.lib /R:0 /S
:COPY_PROGRAMS
echo ">>>>>>>>>> copy programs"
robocopy "%CMAKE_BUILD_DIR%\Release" "%GRPC_PROGRAMS_DIR%" *.exe /R:0 /S
copy "%CMAKE_BUILD_DIR%\third_party\protobuf\Release\protoc.exe" "%GRPC_PROGRAMS_DIR%\protoc.exe"
:REMOVE_USELESS_LIBRARIES
echo ">>>>>>>>>> remove useless libraries"
del "%GRPC_LIBRARIES_DIR%\grpc_csharp_ext.lib"
del "%GRPC_LIBRARIES_DIR%\gflags_static.lib"
del "%GRPC_LIBRARIES_DIR%\gflags_nothreads_static.lib"
del "%GRPC_LIBRARIES_DIR%\benchmark.lib"
:Finish
echo ">>>>>>>>>> finish"
cd "%SCRIPT_FOLDER%"
GOTO GRACEFULEXIT
:ABORT
pause
echo Aborted...
:GRACEFULEXIT
echo Build done!
================================================
FILE: Setup.command
================================================
#!/bin/bash
# Exit on errors if any
set -e
###############################################################################
# Should be defined as an environment variable, will be v1.3.x otherwise
branch=${branch:-v1.23.x}
clean=${clean:-true}
VAR_GIT_BRANCH=$branch
VAR_CLEAR_REPO=$clean
REMOTE_ORIGIN="https://github.com/grpc/grpc.git"
GOSUPPORT_REMOTE_ORIGIN="https://github.com/golang/protobuf.git"
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
GRPC_FOLDER_NAME=grpc
GRPC_ROOT="${SCRIPT_DIR}/${GRPC_FOLDER_NAME}"
CMAKE_BUILD_DIR="${GRPC_ROOT}/.build"
DEPS=(git automake autoconf libtool make strip clang++ go)
###############################################################################
echo "SCRIPT_DIR=${SCRIPT_DIR}"
echo "GRPC_ROOT=${GRPC_ROOT}"
UE_ROOT=${UE_ROOT:-"/var/lib/jenkins/UE_4.20.2-release"}
if [ ! -d "$UE_ROOT" ]; then
echo "UE_ROOT directory ${UE_ROOT} does not exist, please set correct UE_ROOT"
exit 1
fi;
# Check if all tools are installed
for i in ${DEPS[@]}; do
if [ ! "$(which ${i})" ];then
echo "${i} not found, install via 'brew install ${i}'" && exit 1
fi
done
# Check if ran under Darwin
if [ $(uname) != 'Darwin' ]; then
echo "Can not work under $(uname) operating system, should be Darwin! Exiting..."
exit 1
fi;
# Clone or pull
if [ ! -d "$GRPC_ROOT" ]; then
echo "Cloning repo into ${GRPC_ROOT}"
git clone $REMOTE_ORIGIN $GRPC_ROOT
else
# [[ ${VAR_CLEAR_REPO} ]] && cd $GRPC_ROOT && git merge --abort || true; git clean -fdx && git checkout -f .
echo "Pulling repo"
(cd $GRPC_ROOT && git pull)
fi
echo "Checking out branch ${VAR_GIT_BRANCH}"
(cd $GRPC_ROOT && git fetch)
(cd $GRPC_ROOT && git checkout -f)
(cd $GRPC_ROOT && git checkout -t origin/$VAR_GIT_BRANCH || true)
# Update submodules
(cd $GRPC_ROOT && git submodule update --init)
if [ "$VAR_CLEAR_REPO" = "true" ]; then
echo "Cleaning repo and submodules because VAR_CLEAR_REPO is set to ${VAR_CLEAR_REPO}"
(cd $GRPC_ROOT && make clean)
(cd $GRPC_ROOT && git clean -fdx)
(cd $GRPC_ROOT && git submodule foreach git clean -fdx)
elif [ "$VAR_CLEAR_REPO" = "false" ]; then
echo "Cleaning is not needed!"
else
echo "Undefined behaviour, VAR_CLEAR_REPO is ${VAR_CLEAR_REPO}!"
exit 1
fi
# Copy INCLUDE folders, should copy:
# - grpc/include
# - grpc/third_party/protobuf/src
HEADERS_DIR="${SCRIPT_DIR}/GrpcIncludes"
PROTOBUF_SRC_DIR="${HEADERS_DIR}/third_party/protobuf"
# (re)-create headers directory
if [ -d "$HEADERS_DIR" ]; then
printf '%s\n' "Removing old $HEADERS_DIR"
rm -rf "$HEADERS_DIR"
fi
mkdir $HEADERS_DIR
mkdir -p $PROTOBUF_SRC_DIR
cp -R "${GRPC_ROOT}/include" $HEADERS_DIR
cp -R "${GRPC_ROOT}/third_party/protobuf/src" $PROTOBUF_SRC_DIR
# Build all
if [ -d "${CMAKE_BUILD_DIR}" ]; then
printf '%s\n' "Removing old ${CMAKE_BUILD_DIR}"
rm -rf "${CMAKE_BUILD_DIR}"
fi
mkdir -p ${CMAKE_BUILD_DIR} && cd ${CMAKE_BUILD_DIR}
echo "BUILD STARTED!"
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CONFIGURATION_TYPES=Release -Dprotobuf_BUILD_TESTS=OFF -DgRPC_ZLIB_PROVIDER=package -DZLIB_INCLUDE_DIR=${UE_ROOT}/Engine/Source/ThirdParty/zlib/v1.2.8/include/Mac -DZLIB_LIBRARY_DEBUG=${UE_ROOT}/Engine/Source/ThirdParty/zlib/v1.2.8/lib/Mac/libz.a -DZLIB_LIBRARY_RELEASE=${UE_ROOT}/Engine/Source/ThirdParty/zlib/v1.2.8/lib/Mac/libz.a -DgRPC_SSL_PROVIDER=package -DLIB_EAY_LIBRARY_DEBUG=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/lib/Mac/Debug/libcrypto.a -DLIB_EAY_LIBRARY_RELEASE=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/lib/Mac/Release/libcrypto.a -DLIB_EAY_DEBUG=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/lib/Mac/Debug/libcrypto.a -DLIB_EAY_RELEASE=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/lib/Mac/Release/libcrypto.a -DOPENSSL_INCLUDE_DIR=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/include/Mac -DSSL_EAY_DEBUG=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/lib/Mac/Debug/libssl.a -DSSL_EAY_LIBRARY_DEBUG=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/lib/Mac/Debug/libssl.a -DSSL_EAY_LIBRARY_RELEASE=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/lib/Mac/Release/libssl.a -DSSL_EAY_RELEASE=${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1.0.2g/lib/Mac/Release/libssl.a
make
cd ${SCRIPT_DIR}
# Copy artifacts
LIBS_DIR="${SCRIPT_DIR}/GrpcLibraries"
BIN_DIR="${SCRIPT_DIR}/GrpcPrograms"
echo "LIBS_DIR is ${LIBS_DIR}"
echo "BIN_DIR is ${BIN_DIR}"
ARCH_LIBS_DIR="${LIBS_DIR}/Mac"
ARCH_BIN_DIR="${BIN_DIR}/Mac"
echo "ARCH_LIBS_DIR is ${ARCH_LIBS_DIR}"
echo "ARCH_BIN_DIR is ${ARCH_BIN_DIR}"
# Remove old libs and binaries directories
if [ -d "$ARCH_LIBS_DIR" ]; then
printf '%s\n' "Removing old $ARCH_LIBS_DIR"
rm -rf "$ARCH_LIBS_DIR"
fi
if [ -d "$ARCH_BIN_DIR" ]; then
printf '%s\n' "Removing old $ARCH_BIN_DIR"
rm -rf "$ARCH_BIN_DIR"
fi
# Create platform-specific artifacts directory
mkdir -p $ARCH_LIBS_DIR
mkdir -p $ARCH_BIN_DIR
SRC_LIBS_FOLDER=${CMAKE_BUILD_DIR}
echo "SRC_LIBS_FOLDER=${SRC_LIBS_FOLDER}"
if [ -d "$SRC_LIBS_FOLDER" ]; then
echo "Copying grpc libraries from ${SRC_LIBS_FOLDER} to ${ARCH_LIBS_DIR}"
(cd $SRC_LIBS_FOLDER && find . -name '*.a' -exec cp -vf '{}' $ARCH_LIBS_DIR ";")
fi
# Strip all symbols from libraries
(cd $ARCH_LIBS_DIR && strip -S *.a)
# Copy binaries (plugins & protoc)
echo "Copying executables to ${ARCH_BIN_DIR}"
cp ${SRC_LIBS_FOLDER}/grpc_cpp_plugin ${ARCH_BIN_DIR}/
cp ${SRC_LIBS_FOLDER}/grpc_csharp_plugin ${ARCH_BIN_DIR}/
cp ${SRC_LIBS_FOLDER}/grpc_node_plugin ${ARCH_BIN_DIR}/
cp ${SRC_LIBS_FOLDER}/grpc_objective_c_plugin ${ARCH_BIN_DIR}/
cp ${SRC_LIBS_FOLDER}/grpc_php_plugin ${ARCH_BIN_DIR}/
cp ${SRC_LIBS_FOLDER}/grpc_python_plugin ${ARCH_BIN_DIR}/
cp ${SRC_LIBS_FOLDER}/grpc_ruby_plugin ${ARCH_BIN_DIR}/
cp ${SRC_LIBS_FOLDER}/third_party/protobuf/protoc ${ARCH_BIN_DIR}/
GOROOT_DIR="${GRPC_ROOT}/go_packages"
GOPROTO_DIR="${GOROOT_DIR}/src/github.com/golang/protobuf"
echo "Building golang support in ${GOPROTO_DIR}"
if [ ! -d "${GOPROTO_DIR}" ]; then
(cd $GRPC_ROOT && git clone $GOSUPPORT_REMOTE_ORIGIN $GOPROTO_DIR)
else
(cd $GOPROTO_DIR && git pull)
fi
# Add gopath with protobuf libs
export GOPATH=$GOROOT_DIR
# Run go build
(cd "${GOPROTO_DIR}/protoc-gen-go" && go build)
(cp "${GOPROTO_DIR}/protoc-gen-go/protoc-gen-go" $ARCH_BIN_DIR)
# Finally, strip binaries (programs)
(cd $ARCH_BIN_DIR && strip -S *)
echo 'BUILD DONE!'
================================================
FILE: Setup.sh
================================================
#!/bin/bash
# Exit on errors if any
set -e
###############################################################################
# Should be defined as an environment variable, will be v1.3.x otherwise
branch=${branch:-v1.23.x}
clean=${clean:-true}
VAR_GIT_BRANCH=$branch
VAR_CLEAR_REPO=$clean
REMOTE_ORIGIN="https://github.com/grpc/grpc.git"
GOSUPPORT_REMOTE_ORIGIN="https://github.com/golang/protobuf.git"
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
GRPC_FOLDER_NAME=grpc
GRPC_ROOT="${SCRIPT_DIR}/${GRPC_FOLDER_NAME}"
DEPS=(git automake autoconf libtool make strip go pkg-config)
# Linux needs an existing UE installation
UE_ROOT=${UE_ROOT:-"/var/lib/jenkins/UE_4.20.2-release"}
if [ ! -d "$UE_ROOT" ]; then
echo "UE_ROOT directory ${UE_ROOT} does not exist, please set correct UE_ROOT"
exit 1
fi;
UE_PREREQUISITES="${UE_ROOT}/Engine/Extras/ThirdPartyNotUE/SDKs/HostLinux/Linux_x64/v13_clang-7.0.1-centos7/x86_64-unknown-linux-gnu"
###############################################################################
OPENSSL_LIB="${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1_0_2h/lib/Linux/x86_64-unknown-linux-gnu"
OPENSSL_INCLUDE="${UE_ROOT}/Engine/Source/ThirdParty/OpenSSL/1_0_2h/include/Linux/x86_64-unknown-linux-gnu"
echo "SCRIPT_DIR=${SCRIPT_DIR}"
echo "GRPC_ROOT=${GRPC_ROOT}"
# Check if all tools are installed
for i in ${DEPS[@]}; do
if [ ! "$(which ${i})" ];then
echo "${i} not found, install via 'apt-get install ${i}'" && exit 1
fi
done
# Check if ran under Linux
if [ $(uname) != 'Linux' ]; then
echo "Can not work under $(uname) operating system, should be Linux! Exiting..."
exit 1
fi;
# Clone or pull
if [ ! -d "$GRPC_ROOT" ]; then
echo "Cloning repo into ${GRPC_ROOT}"
git clone $REMOTE_ORIGIN $GRPC_ROOT
else
# [[ ${VAR_CLEAR_REPO} ]] && cd $GRPC_ROOT && git merge --abort || true; git clean -fdx && git checkout -f .
echo "Pulling repo"
(cd $GRPC_ROOT && git pull)
fi
echo "Checking out branch ${VAR_GIT_BRANCH}"
(cd $GRPC_ROOT && git fetch)
(cd $GRPC_ROOT && git checkout -f)
(cd $GRPC_ROOT && git checkout -t origin/$VAR_GIT_BRANCH || true)
# Update submodules
(cd $GRPC_ROOT && git submodule update --init)
if [ "$VAR_CLEAR_REPO" = "true" ]; then
echo "Cleaning repo and submodules because VAR_CLEAR_REPO is set to ${VAR_CLEAR_REPO}"
(cd $GRPC_ROOT && make clean)
(cd $GRPC_ROOT && git clean -fdx)
(cd $GRPC_ROOT && git submodule foreach git clean -fdx)
elif [ "$VAR_CLEAR_REPO" = "false" ]; then
echo "Cleaning is not needed!"
else
echo "Undefined behaviour, VAR_CLEAR_REPO is ${VAR_CLEAR_REPO}!"
exit 1
fi
# Copy INCLUDE folders, should copy:
# - grpc/include
# - grpc/third_party/protobuf/src
HEADERS_DIR="${SCRIPT_DIR}/GrpcIncludes"
PROTOBUF_SRC_DIR="${HEADERS_DIR}/third_party/protobuf"
# (re)-create headers directory
if [ -d "$HEADERS_DIR" ]; then
printf '%s\n' "Removing old $HEADERS_DIR"
rm -rf "$HEADERS_DIR"
fi
mkdir $HEADERS_DIR
mkdir -p $PROTOBUF_SRC_DIR
cp -R "${GRPC_ROOT}/include" $HEADERS_DIR
cp -R "${GRPC_ROOT}/third_party/protobuf/src" $PROTOBUF_SRC_DIR
# Compute arch string using uname
UNAME_MACH=$(echo $(uname -m) | tr '[:upper:]' '[:lower:]')
UNAME_OS=$(echo $(uname) | tr '[:upper:]' '[:lower:]')
UNAME_ARCH="${UNAME_MACH}-unknown-${UNAME_OS}-gnu"
LIBCXX_UE_DIR="${UE_ROOT}/Engine/Source/ThirdParty/Linux/LibCxx/include"
LIBC_UE_DIR="${UE_ROOT}/Engine/Source/ThirdParty/Linux/LibCxx/include"
export CC="${UE_PREREQUISITES}/bin/clang"
export CC_FOR_BUILD=${CC}
export CXX="${UE_PREREQUISITES}/bin/clang++"
export CXX_FOR_BUILD=${CXX}
# we need this to avoid 'unknow flavor: old-gnu' error
if [ ! -e "${UE_PREREQUISITES}/bin/lld-gnu" ]; then
ln -s "${UE_PREREQUISITES}/bin/ld.lld" "${UE_PREREQUISITES}/bin/lld-gnu"
fi
find "${UE_PREREQUISITES}/usr/lib64" -name '*.o' -exec cp -vfs '{}' "${UE_PREREQUISITES}/lib64" ";"
# this thing avoid us from gcc usage, we don't need it
export VALID_CONFIG_gcov=0
# force compile protobuf, libz and libares
export HAS_SYSTEM_CARES=false
export HAS_SYSTEM_PROTOBUF=false
export HAS_SYSTEM_ZLIB=false
# funny, but in grpc Makefile LD and LDXX associated with compilers
export LD="${CC}"
export LDXX="${CXX}"
export DEFAULT_CC="${CC}"
export DEFAULT_CXX="${CXX}"
export CFLAGS="-fPIC -Wno-error --sysroot=${UE_PREREQUISITES}"
export CFLAGS_FOR_BUILD=${CFLAGS}
export CXXFLAGS="-std=c++14 -fPIC -nostdinc++ -Wno-expansion-to-defined -Wno-error -I${LIBCXX_UE_DIR} -I${LIBCXX_UE_DIR}/c++/v1 -I${OPENSSL_INCLUDE}"
export CXXFLAGS_FOR_BUILD=${CXXFLAGS}
export LIBRARY_PATH="${UE_PREREQUISITES}/usr/lib64"
export LDFLAGS="-L${UE_ROOT}/Engine/Source/ThirdParty/Linux/LibCxx/lib/Linux/${UNAME_ARCH} -L${OPENSSL_LIB} -fuse-ld=${UE_PREREQUISITES}/bin/lld-gnu"
export LDFLAGS_FOR_BUILD=${LDFLAGS}
export LDLIBS="-lc++ -lc++abi -lc"
export PROTOBUF_LDFLAGS_EXTRA="${LDFLAGS} ${LDLIBS}"
# Create an alias 'clocale -> xlocale.h' (if does not exist)
if [ ! -e "${LIBCXX_UE_DIR}/c++/v1/xlocale.h" ]; then
if [ ! -e "${LIBCXX_UE_DIR}/c++/v1/clocale" ]; then
echo "${LIBCXX_UE_DIR}/c++/v1/clocale must exist in UE src dir. Exiting..." && exit 1
fi
(cd "${LIBCXX_UE_DIR}/c++/v1" && ln -s clocale xlocale.h)
echo "Created an alias to xlocale.h"
fi
echo "CFLAGS=${CFLAGS}, CXXFLAGS=${CXXFLAGS}, LDFLAGS=${LDFLAGS}, LDLIBS=${LDLIBS}, PROTOBUF_LDFLAGS_EXTRA=${PROTOBUF_LDFLAGS_EXTRA}"
# Build GRPC
(cd $GRPC_ROOT && make CC=${CC} CXX=${CXX})
# Copy artifacts
LIBS_DIR="${SCRIPT_DIR}/GrpcLibraries"
BIN_DIR="${SCRIPT_DIR}/GrpcPrograms"
echo "LIBS_DIR is ${LIBS_DIR}"
echo "BIN_DIR is ${BIN_DIR}"
if [ $(uname) != 'Darwin' ]; then
ARCH_LIBS_DIR="${LIBS_DIR}/"$(uname)
ARCH_BIN_DIR="${BIN_DIR}/"$(uname)
else
ARCH_LIBS_DIR="${LIBS_DIR}/Mac"
ARCH_BIN_DIR="${BIN_DIR}/Mac"
fi
echo "ARCH_LIBS_DIR is ${ARCH_LIBS_DIR}"
echo "ARCH_BIN_DIR is ${ARCH_BIN_DIR}"
# Remove old libs and binaries directories
if [ -d "$ARCH_LIBS_DIR" ]; then
printf '%s\n' "Removing old $ARCH_LIBS_DIR"
rm -rf "$ARCH_LIBS_DIR"
fi
if [ -d "$ARCH_BIN_DIR" ]; then
printf '%s\n' "Removing old $ARCH_BIN_DIR"
rm -rf "$ARCH_BIN_DIR"
fi
# Create platform-specific artifacts directory
mkdir -p $ARCH_LIBS_DIR
mkdir -p $ARCH_BIN_DIR
SRC_LIBS_FOLDER_GRPC=$GRPC_ROOT/libs/opt
SRC_LIBS_FOLDER_PROTOBUF=$PROTOBUF_ROOT/src/.libs
# Force recursively copy
if [ -d "$SRC_LIBS_FOLDER_PROTOBUF" ]; then
echo "Copying protobuf libraries from ${SRC_LIBS_FOLDER_PROTOBUF} to ${ARCH_LIBS_DIR}"
(cd $SRC_LIBS_FOLDER_PROTOBUF && find . -name '*.a' -exec cp -vf '{}' $ARCH_LIBS_DIR ";")
fi
if [ -d "$SRC_LIBS_FOLDER_GRPC" ]; then
echo "Copying grpc libraries from ${SRC_LIBS_FOLDER_GRPC} to ${ARCH_LIBS_DIR}"
(cd $SRC_LIBS_FOLDER_GRPC && find . -name '*.a' -exec cp -vf '{}' $ARCH_LIBS_DIR ";")
fi
# Strip all symbols from libraries
(cd $ARCH_LIBS_DIR && strip -S *.a)
# Copy binaries (plugins & protoc)
echo "Copying executables to ${ARCH_BIN_DIR}"
(cp -a "${GRPC_ROOT}/bins/opt/." $ARCH_BIN_DIR)
(cp -a "${GRPC_ROOT}/bins/opt/protobuf/." $ARCH_BIN_DIR)
# This seems to be a hack, should modify (cp -a "${GRPC_ROOT}/bins/opt/." $BIN_DIR) to copy only files, bot dirs
(cd $ARCH_BIN_DIR && rm -rf protobuf)
#
# Build go support
GOROOT_DIR="${GRPC_ROOT}/go_packages"
GOPROTO_DIR="${GOROOT_DIR}/src/github.com/golang/protobuf"
echo "Building golang support in ${GOPROTO_DIR}"
if [ ! -d "${GOPROTO_DIR}" ]; then
(cd $GRPC_ROOT && git clone $GOSUPPORT_REMOTE_ORIGIN $GOPROTO_DIR)
else
(cd $GOPROTO_DIR && git pull)
fi
# Add gopath with protobuf libs
export GOPATH=$GOROOT_DIR
#
# Run go build
(cd "${GOPROTO_DIR}/protoc-gen-go" && go build)
(cp "${GOPROTO_DIR}/protoc-gen-go/protoc-gen-go" $ARCH_BIN_DIR)
# Strip binaries (programs)
(cd $ARCH_BIN_DIR && strip -S *)
# Finnaly, clean all stuff
rm "${LIBCXX_UE_DIR}/c++/v1/xlocale.h"
rm "${UE_PREREQUISITES}/bin/lld-gnu"
find "${UE_PREREQUISITES}/lib64" -name '*.o' -type f -delete
# Copy source
echo 'BUILD DONE!'
================================================
FILE: Source/InfraworldRuntime/InfraWorldRuntime.Build.cs
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
// #define TRACE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
using UnrealBuildTool;
public class InfraworldRuntime : ModuleRules
{
private UnrealTargetPlatform Platform;
private UnrealTargetConfiguration Configuration;
// name of root folders in the project folder
private static readonly string GRPC_STRIPPED_FOLDER = "GrpcIncludes";
private static readonly string GRPC_LIBS_FOLDER = "GrpcLibraries";
private string INCLUDE_ROOT;
private string LIB_ROOT;
public class ModuleDepPaths
{
public readonly string[] HeaderPaths;
public readonly string[] LibraryPaths;
public ModuleDepPaths(string[] headerPaths, string[] libraryPaths)
{
HeaderPaths = headerPaths;
LibraryPaths = libraryPaths;
}
public override string ToString()
{
return "Headers:\n" + string.Join("\n", HeaderPaths) + "\nLibs:\n" + string.Join("\n", LibraryPaths);
}
}
[Conditional("DEBUG")]
[Conditional("TRACE")]
private void clog(params object[] objects)
{
Console.WriteLine(string.Join(", ", objects));
}
private IEnumerable<string> FindFilesInDirectory(string dir, string suffix = "")
{
List<string> matches = new List<string>();
if (Directory.Exists(dir))
{
string[] files = Directory.GetFiles(dir);
Regex regex = new Regex(".+\\." + suffix);
foreach (string file in files)
{
if (regex.Match(file).Success)
matches.Add(file);
}
}
return matches;
}
private string GetConfigurationString()
{
return (Configuration == UnrealTargetConfiguration.Shipping) ? "Release" : "Debug";
}
public ModuleDepPaths GatherDeps()
{
string RootPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../../"));
INCLUDE_ROOT = Path.Combine(RootPath, GRPC_STRIPPED_FOLDER);
LIB_ROOT = Path.Combine(RootPath, GRPC_LIBS_FOLDER);
List<string> headers = new List<string>();
List<string> libs = new List<string>();
string PlatformLibRoot = "";
if (Platform == UnrealTargetPlatform.Win64)
{
PlatformLibRoot = Path.Combine(LIB_ROOT, Platform.ToString());
libs.AddRange(FindFilesInDirectory(PlatformLibRoot, "lib"));
}
else
{
PlatformLibRoot = Path.Combine(LIB_ROOT, Platform.ToString());
libs.AddRange(FindFilesInDirectory(PlatformLibRoot, "a"));
}
clog("PlatformLibRoot: " + PlatformLibRoot);
headers.Add(Path.Combine(INCLUDE_ROOT, "include"));
headers.Add(Path.Combine(INCLUDE_ROOT, "third_party", "protobuf", "src"));
return new ModuleDepPaths(headers.ToArray(), libs.ToArray());
}
public InfraworldRuntime(ReadOnlyTargetRules Target) : base(Target)
{
PublicDefinitions.Add("GOOGLE_PROTOBUF_NO_RTTI");
PublicDefinitions.Add("GPR_FORBID_UNREACHABLE_CODE");
PublicDefinitions.Add("GRPC_ALLOW_EXCEPTIONS=0");
//TODO: We do this because in file generated_message_table_driven.h that located in protobuf sources
//TODO: line 174: static_assert(std::is_pod<AuxillaryParseTableField>::value, "");
//TODO: causes С4647 level 3 warning __is_pod behavior change
//TODO: UE4 threading some warnings as errors, and we have no chance to suppress this stuff
//TODO: So, we don't want to change any third-party code, this why we add this definition
PublicDefinitions.Add("__NVCC__");
Platform = Target.Platform;
Configuration = Target.Configuration;
ModuleDepPaths moduleDepPaths = GatherDeps();
Console.WriteLine(moduleDepPaths.ToString());
PublicIncludePaths.AddRange(moduleDepPaths.HeaderPaths);
PublicAdditionalLibraries.AddRange(moduleDepPaths.LibraryPaths);
PublicDependencyModuleNames.AddRange(new string[] {
"Core"
});
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenSSL");
AddEngineThirdPartyPrivateStaticDependencies(Target, "zlib");
PrivateDependencyModuleNames.AddRange(new string[] { "CoreUObject", "Engine" });
}
}
================================================
FILE: Source/InfraworldRuntime/Private/ChannelCredentials.cpp
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include "ChannelCredentials.h"
#include "InfraworldRuntime.h"
#include "GrpcIncludesBegin.h"
#include <grpc++/security/credentials.h>
#include "GrpcIncludesEnd.h"
UChannelCredentials* UChannelCredentials::MakeGoogleDefaultCredentials()
{
return NewObject<UGoogleDefaultCredentials>();
}
UChannelCredentials* UChannelCredentials::MakeSslCredentials(FString PemRootCerts, FString PemPrivateKey, FString PemCertChain)
{
USslCredentials* const SslCredentials = NewObject<USslCredentials>();
SslCredentials->PemRootCerts = PemRootCerts;
SslCredentials->PemPrivateKey = PemPrivateKey;
SslCredentials->PemCertChain = PemCertChain;
return SslCredentials;
}
UChannelCredentials* UChannelCredentials::MakeInsecureChannelCredentials()
{
return NewObject<UInsecureChannelCredentials>();
}
#if PLATFORM_WINDOWS
#include "Windows/HideWindowsPlatformTypes.h"
#endif
================================================
FILE: Source/InfraworldRuntime/Private/GrpcUriValidator.cpp
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include "GrpcUriValidator.h"
#include "InfraworldRuntime.h"
#include "Misc/DefaultValueHelper.h"
class FGrpcUriValidator_Internal
{
public:
static bool ValidatePort(const FString& MaybePort, FString& OutError);
static bool ValidateIp(const FString& MaybeIpAddress, FString& OutError);
static bool ValidateDomainName(const FString& MaybeDomainName, FString& OutError);
static bool DoesHostLookLikeIp(const FString& MaybeIpAddress);
private:
static bool TCharIsLetter(TCHAR character);
static bool TCharIsDigit(TCHAR character);
};
/// FGrpcUriValidator_Internal interface
bool FGrpcUriValidator_Internal::ValidatePort(const FString& MaybePort, FString& OutError)
{
int32 PortAsInteger;
if (FDefaultValueHelper::ParseInt(MaybePort, PortAsInteger))
{
const TRange<int32> ValidPortRange(0, 0xFFFF);
if (!ValidPortRange.Contains(PortAsInteger))
{
OutError = FString::Printf(TEXT("Invalid port number: \"%d\", must be within [%d - %d)"), PortAsInteger, 0, 0xFFFF);
return false;
}
}
else
{
OutError = FString::Printf(TEXT("Can not parse port \"%s\" into an integer. The format is invalid."), *MaybePort);
return false;
}
return true;
}
bool FGrpcUriValidator_Internal::ValidateIp(const FString& MaybeIpAddress, FString& OutError)
{
TArray<FString> Octets;
MaybeIpAddress.ParseIntoArray(Octets, TEXT("."));
if (Octets.Num() == 4)
{
const TRange<int32> OctetRange(0, 0xFF);
for (const FString& Octet : Octets)
{
int32 Out;
if (FDefaultValueHelper::ParseInt(Octet, Out))
{
if (!OctetRange.Contains(Out))
{
OutError = FString::Printf(TEXT("An octet \"%s\" in the IPv4 address (which is \"%s\") is of range [0 - 256)"), *Octet, *MaybeIpAddress);
return false;
}
}
else
{
OutError = FString::Printf(TEXT("\"%s\" in \"%s\" does not seems to be int32"), *Octet, *MaybeIpAddress);
return false;
}
}
}
else
{
OutError = FString::Printf(TEXT("Can not parse IPv4 address (which is \"%s\") into TArray<FString>, or invalid number of octets"), *MaybeIpAddress);
return false;
}
return true;
}
bool FGrpcUriValidator_Internal::ValidateDomainName(const FString& MaybeDomainName, FString& OutError)
{
for (TCHAR Character : MaybeDomainName)
{
if (!TCharIsLetter(Character) && !TCharIsDigit(Character) && (Character != TEXT('-')) && (Character != TEXT('.')))
{
OutError = FString::Printf(TEXT("\"%s\" domain name contains forbidden character: \"%c\""), *MaybeDomainName, Character);
return false;
}
}
return true;
}
bool FGrpcUriValidator_Internal::DoesHostLookLikeIp(const FString& MaybeIpAddress)
{
for (TCHAR Character : MaybeIpAddress)
{
if (!TCharIsDigit(Character) && (Character != TEXT('.')))
return false;
}
return true;
}
bool FGrpcUriValidator_Internal::TCharIsLetter(TCHAR character)
{
const bool bUpperCase = (character >= TEXT('A')) && (character <= TEXT('Z'));
const bool bLowerCase = (character >= TEXT('a')) && (character <= TEXT('z'));
return bUpperCase || bLowerCase;
}
bool FGrpcUriValidator_Internal::TCharIsDigit(TCHAR character)
{
return (character >= TEXT('0')) && (character <= TEXT('9'));
}
/// FGrpcUriValidator interface
bool FGrpcUriValidator::Validate(const FString& MaybeGrpcUri, FString& OutError)
{
static const FString SchemeSeparator(TEXT("://"));
const int32 IndexOfSchemeSeparator = MaybeGrpcUri.Find(SchemeSeparator);
if (IndexOfSchemeSeparator >= 0)
{
const FString& Scheme = MaybeGrpcUri.Mid(0, IndexOfSchemeSeparator);
OutError = FString::Printf(TEXT("GRPC URI \"%s\" must not contain a URL scheme (\"%s\" provided). GRPC forbids explicit schemes."), *MaybeGrpcUri, *Scheme);
return false;
}
const int32 PathSeparatorIndex = MaybeGrpcUri.Find(TEXT("/"));
const bool bHasPathSeparator = PathSeparatorIndex >= 0;
const int32 PortSeparatorIndex = MaybeGrpcUri.Find(TEXT(":"), ESearchCase::IgnoreCase, ESearchDir::FromEnd, (PathSeparatorIndex >= 0) ? PathSeparatorIndex : INDEX_NONE);
const bool bHasPortSeparator = PortSeparatorIndex >= 0;
FString GrpcHostName = TEXT("");
FString GrpcPort = TEXT("80");
if (bHasPortSeparator)
{
GrpcHostName = MaybeGrpcUri.Mid(0, PortSeparatorIndex);
const int32 PortSubstringStart = PortSeparatorIndex + 1;
if (bHasPathSeparator)
{
GrpcPort = MaybeGrpcUri.Mid(PortSubstringStart, (PathSeparatorIndex - PortSubstringStart));
}
else
{
GrpcPort = MaybeGrpcUri.Mid(PortSubstringStart);
}
}
else
{
if (bHasPathSeparator)
{
GrpcHostName = MaybeGrpcUri.Mid(0, PathSeparatorIndex);
}
else
{
GrpcHostName = MaybeGrpcUri;
}
}
if (bHasPathSeparator)
{
const FString& RestOfAddress = MaybeGrpcUri.Mid(PathSeparatorIndex);
if (!RestOfAddress.IsEmpty())
{
OutError = FString::Printf(TEXT("Path of the \"%s\" uri, must be empty. Actually it is: \"%s\""), *MaybeGrpcUri, *RestOfAddress);
return false;
}
}
if (FGrpcUriValidator_Internal::DoesHostLookLikeIp(GrpcHostName))
{
// Validate as IP address
if (!FGrpcUriValidator_Internal::ValidateIp(GrpcHostName, OutError))
return false;
}
else
{
// Validate as domain name
if (!FGrpcUriValidator_Internal::ValidateDomainName(GrpcHostName, OutError))
return false;
}
// Anyway, validate port
if (!FGrpcUriValidator_Internal::ValidatePort(GrpcPort, OutError))
return false;
return true;
}
================================================
FILE: Source/InfraworldRuntime/Private/InfraworldRuntime.cpp
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include "InfraworldRuntime.h"
DEFINE_LOG_CATEGORY(LogInfraworldRuntime);
#define LOCTEXT_NAMESPACE "FInfraworldRuntimeModule"
void FInfraworldRuntimeModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FInfraworldRuntimeModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FInfraworldRuntimeModule, InfraworldRuntime)
================================================
FILE: Source/InfraworldRuntime/Private/RpcClient.cpp
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include "RpcClient.h"
#include "InfraworldRuntime.h"
#include "RpcClientWorker.h"
#include "GrpcUriValidator.h"
#include "Containers/Ticker.h"
#include "Misc/CoreDelegates.h"
#include "Misc/DefaultValueHelper.h"
#include "HAL/RunnableThread.h"
#include "Kismet/KismetStringLibrary.h"
// ============ RpcClient implementation ===========
bool URpcClient::Init(const FString& URI, UChannelCredentials* ChannelCredentials)
{
if (bCanSendRequests)
{
UE_LOG(LogInfraworldRuntime, Error, TEXT("You're trying to initialize an RPC Client more than once"));
return true;
}
FString ErrorMessage;
if (!FGrpcUriValidator::Validate(URI, ErrorMessage))
{
UE_LOG(LogInfraworldRuntime, Error, TEXT("%s Unable to validate URI: %s"), *(GetClass()->GetName()), *ErrorMessage);
}
// Do it if and only if the thread is not yet created.
if (Thread == nullptr)
{
UE_LOG(LogInfraworldRuntime, Log, TEXT("RpcClient at [%p], Thread == nullptr, initializing"), this);
// Launch 'chaining' hierarchical init, which will init a superclass (a concrete implementation).
HierarchicalInit();
UE_LOG(LogInfraworldRuntime, Log, TEXT("RpcClient at [%p], finished HierarchicalInit"), this);
// Retrieve and set an Error Message Queue
if (InnerWorker)
{
UE_LOG(LogInfraworldRuntime, Log, TEXT("RpcClient at [%p], InnerWorker = %p"), this, InnerWorker.Get());
InnerWorker->URI = URI;
InnerWorker->ChannelCredentials = ChannelCredentials;
InnerWorker->ErrorMessageQueue = &ErrorMessageQueue;
const FString ThreadName(FString::Printf(TEXT("RPC Client Thread %s %d"), *(GetClass()->GetName()), FMath::RandRange(0, TNumericLimits<int32>::Max())));
Thread = FRunnableThread::Create(InnerWorker.Get(), *ThreadName);
bCanSendRequests = true;
UE_LOG(LogInfraworldRuntime, Log, TEXT("Just made a thread: %s, address %p"), *ThreadName, InnerWorker.Get());
}
else
{
UE_LOG(LogInfraworldRuntime, Fatal, TEXT("An inner worker of %s wasn't initialized"), *(GetClass()->GetName()));
}
}
if (CanSendRequests())
{
TickDelegateHandle = FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([this](float)
{
if (!ErrorMessageQueue.IsEmpty())
{
FRpcError ReceivedError;
ErrorMessageQueue.Dequeue(ReceivedError);
EventRpcError.Broadcast(this, ReceivedError);
// No need to call URpcClient::HierarchicalUpdate() if got any errors (Errors first)
}
else
{
HierarchicalUpdate();
}
return true;
}));
}
return bCanSendRequests;
}
URpcClient::URpcClient() : InnerWorker(nullptr), TickDelegateHandle()
{
}
URpcClient::~URpcClient()
{
UE_LOG(LogInfraworldRuntime, Verbose, TEXT("An instance of RPC Client has been destroyed. Still can send requests: %s"),
*UKismetStringLibrary::Conv_BoolToString(CanSendRequests()));
}
void URpcClient::Update()
{
// Occasionally left blank
}
bool URpcClient::CanSendRequests() const
{
return bCanSendRequests;
}
URpcClient* URpcClient::CreateRpcClient(TSubclassOf<URpcClient> Class, FRpcClientInstantiationParameters InstantiationParameters, UObject* Outer)
{
const FString& URI = FString::Printf(TEXT("%s:%d"), *(InstantiationParameters.Ip), InstantiationParameters.Port);
return CreateRpcClientUri(Class, URI, InstantiationParameters.ChannelCredentials, Outer);
}
URpcClient* URpcClient::CreateRpcClientUri(TSubclassOf<URpcClient> Class, const FString& URI, UChannelCredentials* ChannelCredentials, UObject* Outer)
{
UObject* const RealOuter = Outer ? Outer : (UObject*)GetTransientPackage();
if (URpcClient* const CreatedClient = NewObject<URpcClient>(RealOuter, *Class))
{
UE_LOG(LogInfraworldRuntime, Log, TEXT("Created RpcClient at [%p] with outer [%p]"), CreatedClient, RealOuter);
bool IsClientInitialized = CreatedClient->Init(URI, ChannelCredentials);
if (!IsClientInitialized)
{
UE_LOG(LogInfraworldRuntime, Error, TEXT("Unable to initialize an RPC client (%s::Init() failed"), *(Class->GetName()));
return nullptr;
}
else
{
UE_LOG(LogInfraworldRuntime, Verbose, TEXT("An instance of %s has been created and initialized"), *(Class->GetName()));
return CreatedClient;
}
}
else
{
UE_LOG(LogInfraworldRuntime, Fatal, TEXT("Unable to create an instance of RPC client (NewObject<%s>() failed)"), *(Class->GetName()));
return nullptr;
}
}
void URpcClient::BeginDestroy()
{
// Being called when GC'ed, should be called synchronously.
if (CanSendRequests())
{
Stop(true);
}
Super::BeginDestroy();
}
void URpcClient::Stop(bool bSynchronous)
{
FRunnableThread* ThreadToStop = Thread.Exchange(nullptr);
if (ThreadToStop)
{
if (!InnerWorker->IsPendingStopped())
InnerWorker->MarkPendingStopped();
bCanSendRequests = false;
UE_LOG(LogInfraworldRuntime, Verbose, TEXT("Scheduled to stop %s via setting 'bCanSendRequests = false', address %p"), *(GetClass()->GetName()), InnerWorker.Get());
// Should be synchronous in (almost) any case
ThreadToStop->Kill(bSynchronous);
delete ThreadToStop;
ThreadToStop = nullptr;
FTicker::GetCoreTicker().RemoveTicker(TickDelegateHandle);
}
else
{
UE_LOG(LogInfraworldRuntime, Error, TEXT("Can not call Stop() for an already stopped (or penfing asinchronously stopped) instance of '%s'"), *(GetClass()->GetName()));
}
}
================================================
FILE: Source/InfraworldRuntime/Private/RpcClientWorker.cpp
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include "RpcClientWorker.h"
#include "InfraworldRuntime.h"
#include "HAL/PlatformTime.h"
#include "GenUtils.h"
#include "GrpcIncludesBegin.h"
#include <grpc++/channel.h>
#include <grpc++/create_channel.h>
#include "GrpcIncludesEnd.h"
#include "WorkerUtils.h"
// ========= RpcClientWorker implementation ========
RpcClientWorker::RpcClientWorker() : WorkerState(ERpcWorkerState::PendingInitialization)
{
}
RpcClientWorker::~RpcClientWorker()
{
}
uint32 RpcClientWorker::Run()
{
// If channel has not been created - we set bPendingStopped = true to StopBackground.
if (!ensureAlways(WorkerState.Exchange(ERpcWorkerState::Initializing) == ERpcWorkerState::PendingInitialization))
{
return 2;
}
if (HierarchicalInit())
{
UE_LOG(LogInfraworldRuntime, Log, TEXT("Finished initialization via HierarchicalInit!"));
ERpcWorkerState ExpectedState = ERpcWorkerState::Initializing;
// this will set WorkerState to Working only if no one overwrote it from Initializing
if (!WorkerState.CompareExchange(ExpectedState, ERpcWorkerState::Working))
{
UE_LOG(LogInfraworldRuntime, Log, TEXT("Worker already marked pending stopped. Its state is %d"), static_cast<int>(ExpectedState));
}
}
else
return 1;
// Update until not pending stopped
while (WorkerState == ERpcWorkerState::Working)
{
UE_LOG(LogInfraworldRuntime, Verbose, TEXT("Updating via HierarchicalUpdate()"));
HierarchicalUpdate();
FPlatformProcess::Sleep(0.1f);
}
WorkerState.Exchange(ERpcWorkerState::Shutdown);
return 0;
}
void RpcClientWorker::DispatchError(const FString& ErrorMessage)
{
UE_CLOG(!ErrorMessageQueue, LogInfraworldRuntime, Fatal, TEXT("Can not dispatch an error message, because ErrorMessageQueue is null"));
FRpcError Error;
Error.ErrorMessage = ErrorMessage;
ErrorMessageQueue->Enqueue(Error);
}
#if PLATFORM_WINDOWS
#include "Windows/HideWindowsPlatformTypes.h"
#endif
================================================
FILE: Source/InfraworldRuntime/Public/CastUtils.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "CoreMinimal.h"
#include <string>
#include <functional>
#include <chrono>
#include "GrpcIncludesBegin.h"
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/map.h>
#include <grpc++/client_context.h>
#include "GrpcIncludesEnd.h"
// Should be imported to avoid long chrono-related instructions.
using std::chrono::milliseconds;
using std::chrono::system_clock;
//
// Generic casts. Should be specified in each header file for each generated structure.
//
namespace casts
{
// ~~~~~ Unreal TMap and TArray, protobuf google::protobuf::RepeatedField and google::protobuf::Map) ~~~~~
template<class OutT, class InT>
using _UnrealMap = TMap<OutT, InT>;
template<class OutT, class InT>
using _ProtobufMap = google::protobuf::Map<OutT, InT>;
template<class OutT>
using _UnrealArray = TArray<OutT>;
template<class OutT>
using _ProtobufArray = google::protobuf::RepeatedField<OutT>;
template<class OutT>
using _ProtobufPtrArray = google::protobuf::RepeatedPtrField<OutT>;
// ~~~~~ CAST PROTOTYPES (GENERIC) ~~~~~
template<class OutT, class InT>
FORCEINLINE OutT Proto_Cast(const InT &Item)
{
// WARNING!!!
// If you're receiving an error message, telling you something like:
//
// error: no matching conversion for static_cast from 'xxx' to 'yyy'
// return (OutT) t;
// ^~~~~
// error: no matching function for call to 'Proto_Cast'
// consider creating your own template specification of Proto_Cast<?>()
// because by default it can only cast statically
return static_cast<OutT>(Item);
}
// ~~~~~ CAST FUNCTIONS (MAPS) ~~~~~
// TMap -> Protobuf Map
template<class OutK, class OutV, class InK, class InV>
FORCEINLINE _UnrealMap<OutK, OutV> Proto_MapCast(const _ProtobufMap<InK, InV>& Map)
{
_UnrealMap<OutK, OutV> OutMap;
for (auto It = Map.cbegin(); It != Map.cend(); ++It)
OutMap.Add(Proto_Cast<OutK>(It->first), Proto_Cast<OutV>(It->second));
return OutMap;
}
// Protobuf Map -> TMap
template<class OutK, class OutV, class InK, class InV>
FORCEINLINE _ProtobufMap<OutK, OutV> Proto_MapCast(const _UnrealMap<InK, InV>& Map)
{
_ProtobufMap<OutK, OutV> OutMap;
for (const TPair<InK, InV>& Pair : Map)
OutMap.insert(google::protobuf::MapPair<OutK, OutV>(Proto_Cast<OutK>(Pair.Key), Proto_Cast<OutV>(Pair.Value)));
return OutMap;
}
// ~~~~~ CAST FUNCTIONS (ARRAYS) ~~~~~
template<class OutT, class InT>
FORCEINLINE _ProtobufArray<OutT> Proto_ArrayCast(const _UnrealArray<InT>& Array)
{
// Allocate a protobuf array and reserve capacity
_ProtobufArray<OutT> OutArray;
OutArray.Reserve((int32)Array.Num());
// Each item shall be individually casted to OutT
for (const InT& Item : Array)
OutArray.Add(Proto_Cast<OutT>(Item));
return OutArray;
}
template<class OutT, class InT>
FORCEINLINE _UnrealArray<OutT> Proto_ArrayCast(const _ProtobufArray<InT>& Array)
{
// Allocate a TArray<OutT> and reserve capacity
_UnrealArray<OutT> OutArray;
OutArray.Reserve((int32)Array.size());
// Each item shall be individually casted to OutT
std::for_each(Array.cbegin(), Array.cend(), [&OutArray](InT Item) {
OutArray.Add(Proto_Cast<OutT>(Item));
});
return OutArray;
}
// Overload for _ProtobufPtrArray (google::protobuf::RepeatedPtrField<?>)
// _ProtobufPtrArray (aka google::protobuf::RepeatedPtrField<OutT>)
// is the same as RepeatedField<?>, but used for repeated strings or messages
template<class OutT, class InT>
FORCEINLINE _ProtobufPtrArray<OutT> Proto_PtrArrayCast(const _UnrealArray<InT>& Array)
{
// Allocate a protobuf array and reserve capacity
_ProtobufPtrArray<OutT> OutArray;
OutArray.Reserve((int32)Array.Num());
// Each item shall be individually casted to OutT
for (const InT& Item : Array)
{
const OutT& cast_result = Proto_Cast<OutT>(Item);
// We need to create a new instance of OutT to be posessed by the OutArray.
OutArray.AddAllocated(new OutT(cast_result));
}
return OutArray;
}
template<class OutT, class InT>
FORCEINLINE _UnrealArray<OutT> Proto_PtrArrayCast(const _ProtobufPtrArray<InT>& Array)
{
// Allocate a TArray<OutT> and reserve capacity
_UnrealArray<OutT> OutArray;
OutArray.Reserve((int32)Array.size());
// Each item shall be individually casted to OutT
std::for_each(Array.cbegin(), Array.cend(), [&OutArray](InT Item) {
OutArray.Add(Proto_Cast<OutT>(Item));
});
return OutArray;
}
// Casting enums value-wise does not require any specialization. Thou can override the template function.
template<typename OutT, typename InT>
FORCEINLINE OutT Proto_EnumCast(const InT &Item)
{
return static_cast<OutT>((int)Item);
};
// ~~~~~ CAST FUNCTIONS (BYTE ARRAY), in protobuf byte arrays are std::strings ~~~~~
template <>
FORCEINLINE FByteArray Proto_Cast(const std::string& String)
{
// Allocate a TArray<uint8>
TArray<uint8> OutArray;
// Put String's content into the array. We can not (and do not need) to cast away const qualifier.
OutArray.Insert(reinterpret_cast<const uint8*>(String.c_str()), String.size(), 0);
// Finally, wrap all data into FByteArray
return FByteArray(OutArray);
}
template <>
FORCEINLINE std::string Proto_Cast(const FByteArray& Item)
{
const TArray<uint8>& Arr = Item.Bytes;
return std::string(reinterpret_cast<const char*>(Arr.GetData()), Arr.Num());
}
// ~~~~~ CAST FUNCTIONS (UNREAL STRING and PROTOBUF STRING) ~~~~~
template <>
FORCEINLINE std::string Proto_Cast(const FString& String)
{
return std::string(TCHAR_TO_UTF8(*String));
}
template <>
FORCEINLINE FString Proto_Cast(const std::string& String)
{
return FString(String.c_str());
}
/**
* Casts an UE4-compatible client context to the GRPC-compatible context.
*
* @note That grpc::ClientContext doesn't have a copy constructor, so grpc::ClientContext can not be returned from a
* ProtoCast<?>(). Thus the method should set an instance of grpc::ClientContext that already exist.
* @param InContext Input UE4-compatible client context.
* @param OutContext Output GRPC-compatible client context.
*/
FORCEINLINE void CastClientContext(const FGrpcClientContext &InContext, grpc::ClientContext &OutContext)
{
// Cast and set metadata, checking for errors.
for (const TPair<FString, FString>& Pair : InContext.Metadata)
{
if (Pair.Key.IsEmpty())
{
UE_LOG(LogTemp, Error, TEXT("Metadata key is empty for mapping '%s'->'%s' and thus won't be added to the client context. Behaviour is restricted by %s"),
*Pair.Key, *Pair.Value, TEXT("grpc/core/lib/surface/validate_metadata:80"));
}
else if (Pair.Key.StartsWith(":"))
{
UE_LOG(LogTemp, Error, TEXT("Metadata key statrs with ':' for mapping '%s'->'%s' and thus won't be added to the client context. Behaviour is restricted by %s"),
*Pair.Key, *Pair.Value, TEXT("grpc/core/lib/surface/validate_metadata:84"));
}
else
{
OutContext.AddMetadata(casts::Proto_Cast<std::string>(Pair.Key), casts::Proto_Cast<std::string>(Pair.Value));
}
// TODO: Add some other validation checks if necessary.
}
// Set deadline (Only if it has a positive value. It is -1 by default)
if (InContext.DeadlineSeconds > .0f)
{
const int64 Milliseconds = static_cast<int64>(((double)InContext.DeadlineSeconds * 1000.0));
OutContext.set_deadline(system_clock::now() + milliseconds(Milliseconds));
}
// Set boolean parameters
OutContext.set_idempotent(InContext.bIdempotent);
OutContext.set_cacheable(InContext.bCacheable);
OutContext.set_wait_for_ready(InContext.bWaitForReady);
// Set authority
OutContext.set_authority(casts::Proto_Cast<std::string>(InContext.Authority));
// Set Compression Algorithm
OutContext.set_compression_algorithm(Proto_EnumCast<grpc_compression_algorithm>(InContext.GrpcCompressionAlgorithm));
// Set Initial Metadata Corked
OutContext.set_initial_metadata_corked(InContext.bInitialMetadataCorked);
}
FORCEINLINE void CastStatus(const grpc::Status& InStatus, FGrpcStatus& OutStatus)
{
OutStatus.ErrorCode = Proto_EnumCast<EGrpcStatusCode>(InStatus.error_code());
OutStatus.ErrorMessage = Proto_Cast<FString>(InStatus.error_message());
OutStatus.ErrorDetails = Proto_Cast<FString>(InStatus.error_details());
}
// Since we have no support for unsigned types in Blueprints, we need to
template <>
FORCEINLINE _UnrealArray<int32> Proto_ArrayCast(const _ProtobufArray<uint32>& Array)
{
// Allocate a TArray<OutT> and reserve capacity
_UnrealArray<int32> OutArray;
OutArray.Reserve((int32)Array.size());
// Each item shall be individually casted to OutT
std::for_each(Array.cbegin(), Array.cend(), [&OutArray](uint32 Item) {
OutArray.Add(Proto_Cast<int32>(Item));
});
return OutArray;
}
template <>
FORCEINLINE _ProtobufArray<uint32> Proto_ArrayCast(const _UnrealArray<int32>& Array)
{
// Allocate a protobuf array and reserve capacity
_ProtobufArray<uint32> OutArray;
OutArray.Reserve((int32)Array.Num());
// Each item shall be individually casted to OutT
for (const int32 Item : Array)
OutArray.Add(Proto_Cast<uint32>(Item));
return OutArray;
}
}
================================================
FILE: Source/InfraworldRuntime/Public/ChannelCredentials.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include <memory>
#include "ChannelCredentials.generated.h"
// namespace grpc
// {
// class ChannelCredentials;
// }
USTRUCT(BlueprintType)
struct INFRAWORLDRUNTIME_API FRpcError
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly, Category=Grpc)
FString ErrorMessage;
};
/**
* A channel credentials object encapsulates all the state needed by a client
* to authenticate with a server for a given channel.
* It can make various assertions, e.g., about the client’s identity, role
* for all the calls on that channel.
*/
UCLASS(NotBlueprintable, NotBlueprintType, notplaceable, noteditinlinenew, hidedropdown, Transient, Abstract)
class INFRAWORLDRUNTIME_API UChannelCredentials : public UObject
{
GENERATED_BODY()
public:
/**
* Builds credentials with reasonable defaults.
*
* \warning Only use these credentials when connecting to a Google endpoint.
* Using these credentials to connect to any other service may result in this
* service being able to impersonate your client for requests to Google
* services.
* @return Google Default Credentials
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Vizor|RPC Credentials")
static UChannelCredentials* MakeGoogleDefaultCredentials();
/**
* Builds SSL Credentials given SSL specific options.
*
* @param PemRootCerts
* The buffer containing the PEM encoding of the server root certificates. If
* this parameter is empty, the default roots will be used. The default
* roots can be overridden using the \a GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
* environment variable pointing to a file on the file system containing the
* roots.
* @param PemPrivateKey
* The buffer containing the PEM encoding of the client's private key. This
* parameter can be empty if the client does not have a private key.
* @param PemCertChain
* The buffer containing the PEM encoding of the client's certificate chain.
* This parameter can be empty if the client does not have a certificate
* chain.
* @return Ssl Credentials
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Vizor|RPC Credentials")
static UChannelCredentials* MakeSslCredentials(
UPARAM(DisplayName="PEM Root Certificates") FString PemRootCerts,
UPARAM(DisplayName="PEM Private Key") FString PemPrivateKey,
UPARAM(DisplayName="PEM Certificate Chain") FString PemCertChain
);
/**
* Builds credentials for an unencrypted, unauthenticated channel.
*
* @return Insecure Channel Credentials
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Vizor|RPC Credentials")
static UChannelCredentials* MakeInsecureChannelCredentials();
};
/**
* Builds credentials with reasonable defaults.
*
* \warning Only use these credentials when connecting to a Google endpoint.
* Using these credentials to connect to any other service may result in this
* service being able to impersonate your client for requests to Google
* services.
*/
UCLASS()
class INFRAWORLDRUNTIME_API UGoogleDefaultCredentials : public UChannelCredentials
{
GENERATED_BODY()
public:
};
/**
* Builds SSL Credentials given SSL specific options
*/
UCLASS()
class INFRAWORLDRUNTIME_API USslCredentials : public UChannelCredentials
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Transient, DisplayName="PEM Root Certificates", Category=GrpcCerts)
FString PemRootCerts;
UPROPERTY(BlueprintReadOnly, Transient, DisplayName="PEM Private Key", Category=GrpcCerts)
FString PemPrivateKey;
UPROPERTY(BlueprintReadOnly, Transient, DisplayName="PEM Certificate Chain", Category=GrpcCerts)
FString PemCertChain;
};
/**
* Credentials for an unencrypted, unauthenticated channel
*/
UCLASS()
class INFRAWORLDRUNTIME_API UInsecureChannelCredentials : public UChannelCredentials
{
GENERATED_BODY()
public:
};
/**
* Instantiation parameters are used to create an RPC client.
*/
USTRUCT(BlueprintType)
struct INFRAWORLDRUNTIME_API FRpcClientInstantiationParameters
{
GENERATED_BODY()
/**
* The IP address of the endpoint to connect to.
*/
UPROPERTY(BlueprintReadWrite, Category=Endpoint)
FString Ip;
/**
* The port of the endpoint to connect to.
*/
UPROPERTY(BlueprintReadWrite, Category=EndpointPort)
int32 Port;
/**
* Credentials to use for the created RPC client. If it does not hold
* an object or is invalid, an error will be thrown.
*/
UPROPERTY(BlueprintReadWrite, Category=Credentials)
UChannelCredentials* ChannelCredentials;
/**
* Gets a GRPC URI for current ip address and port.
*
* @return URI for channel instantiation.
*/
FString GetURI() const
{
return FString::Printf(TEXT("%s:%d"), *Ip, Port);
}
/**
* Gets a string representation of current FRpcClientInstantiationParameters.
*
* @return FRpcClientInstantiationParameters string representation.
*/
FString GetName() const
{
const FString& ParamsURI = GetURI();
const FString& CredentialsClassName = ChannelCredentials ? *(ChannelCredentials->GetClass()->GetName()) : TEXT("nullptr (DANGER!)");
return FString::Printf(TEXT("URI: %s, Credentials: %s"), *ParamsURI, *CredentialsClassName);
}
};
================================================
FILE: Source/InfraworldRuntime/Public/ChannelProvider.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include <grpc++/channel.h>
#include <grpc++/create_channel.h>
#include <grpc++/security/credentials.h>
#include "RpcClientWorker.h"
#include "ChannelCredentials.h"
namespace channel
{
FORCEINLINE bool WaitUntilChannelIsReady(const std::shared_ptr<grpc::Channel>& Channel, std::chrono::system_clock::time_point Deadline)
{
grpc_connectivity_state State = Channel->GetState(true);
while (State != GRPC_CHANNEL_READY)
{
if (!Channel->WaitForStateChange(State, Deadline))
return false;
State = Channel->GetState(true);
}
return true;
}
FORCEINLINE bool WaitForConnection(float Seconds, const std::shared_ptr<grpc::Channel>& Channel)
{
bool IsConnected = false;
const int64 Milliseconds = (int64)((double) Seconds * 1000.0);
std::chrono::system_clock::time_point start_tp = std::chrono::system_clock::now();
std::chrono::system_clock::time_point end_tp = std::chrono::system_clock::now() + std::chrono::milliseconds(Milliseconds);
std::chrono::system_clock::time_point current_tp = start_tp;
while (!IsConnected)
{
std::chrono::system_clock::time_point delta_tp = std::chrono::system_clock::now() + std::chrono::milliseconds(100);
if (current_tp < end_tp)
IsConnected = WaitUntilChannelIsReady(Channel, delta_tp);
else
break;
current_tp = std::chrono::system_clock::now();
}
return IsConnected;
}
FORCEINLINE std::shared_ptr<grpc::ChannelCredentials> GetGrpcCredentials(UChannelCredentials* const Credentials)
{
// Check whether provided credentails are null.
if (!Credentials)
{
UE_LOG(LogTemp, Error,
TEXT("Provided credentials are NULL. (Did you forget to pass ChannelCredentials to instantiation parameters?). Replacement is "
"grpc::InsecureChannelCredentials()."));
return grpc::InsecureChannelCredentials();
}
// Classify the credentials
if (Credentials->IsA<UGoogleDefaultCredentials>())
{
return grpc::GoogleDefaultCredentials();
}
else if (Credentials->IsA<UInsecureChannelCredentials>())
{
return grpc::InsecureChannelCredentials();
}
else if (const USslCredentials* const SslCredentials = Cast<USslCredentials>(Credentials))
{
grpc::SslCredentialsOptions Options;
if (SslCredentials->PemRootCerts.Len() > 0)
Options.pem_root_certs = TCHAR_TO_ANSI(*(SslCredentials->PemRootCerts));
if (SslCredentials->PemPrivateKey.Len() > 0)
Options.pem_private_key = TCHAR_TO_ANSI(*(SslCredentials->PemPrivateKey));
if (SslCredentials->PemCertChain.Len() > 0)
Options.pem_cert_chain = TCHAR_TO_ANSI(*(SslCredentials->PemCertChain));
return grpc::SslCredentials(Options);
}
// Unknown credentials
UE_LOG(LogTemp, Error, TEXT("Don't know how to process credentials:'%s'. Replacement is grpc::InsecureChannelCredentials()."),
*(Credentials->GetClass()->GetName()));
return grpc::InsecureChannelCredentials();
}
FORCEINLINE std::shared_ptr<grpc::Channel> CreateChannel(RpcClientWorker* Worker)
{
UChannelCredentials* const ChannelCredentials = Worker->ChannelCredentials;
UE_CLOG(!ChannelCredentials, LogTemp, Fatal, TEXT("Channel Credentials mustn't be null"));
const FString& URI = Worker->URI;
UE_LOG(LogTemp, Display, TEXT("The following Channel Credentials is used: \"%s\". Connecting to: \"%s\""), *(ChannelCredentials->GetName()), *URI);
std::shared_ptr<grpc::ChannelCredentials> GrpcCredentials = GetGrpcCredentials(ChannelCredentials);
std::shared_ptr<grpc::Channel> Channel = grpc::CreateChannel(TCHAR_TO_ANSI(*URI), GrpcCredentials);
bool bConnectionWasSuccessful = WaitForConnection(3, Channel);
if (!bConnectionWasSuccessful)
{
Worker->DispatchError(
NSLOCTEXT("InfraworldChannelProvider", "InfraworldChannelProviderGrpcServiceConnectionError", "Service connection failure!").ToString());
return std::shared_ptr<grpc::Channel>(nullptr);
}
else
{
UE_LOG(LogTemp, Verbose, TEXT("%s"),
*NSLOCTEXT("InfraworldChannelProvider", "InfraworldChannelProviderGrpcServiceConnectionSuccess", "Service connection established!").ToString());
}
return Channel;
}
}
================================================
FILE: Source/InfraworldRuntime/Public/Conduit.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "CoreMinimal.h"
#include "HAL/PlatformTLS.h"
#include "Containers/Queue.h"
/**
* A conduit is a combination of two channel: The Request channel, and the Response channel, representing bidirectional queue.
* A conduit is optimized to work efficiently and lock-free between two threads: the 'Request writer' thread and the
* 'Response writer' thread.
* One should call Acquire(Requests/Response)Producer() in the thread, which should produce Requests or Responses.
*/
template<class TRequest, class TResponse>
class TConduit
{
FORCEINLINE uint32 ThreadID() const { return FPlatformTLS::GetCurrentThreadId(); }
public:
TConduit() : RequestsProducerID(-1), ResponsesProducerID(-1)
{
}
~TConduit();
/**
* Should be called from a Request producer thread.
* After that:
* - Only THIS thread can call Enqueue(Request),
* - Only THIS thread can call Dequeue(Response),
* - Calling IsEmpty() will tell whether the Request channel is empty.
*/
void AcquireRequestsProducer()
{
RequestsProducerID = ThreadID();
}
/**
* Should be called from a Response producer thread.
* After that:
* - Only THIS thread can call Enqueue(Response),
* - Only THIS thread can call Dequeue(Request),
* - Calling IsEmpty() will tell whether the Response channel is empty.
*/
void AcquireResponsesProducer()
{
ResponsesProducerID = ThreadID();
}
// Enqueue:
bool Enqueue(const TRequest& Item)
{
UE_CLOG(ThreadID() != RequestsProducerID, LogTemp, Fatal, TEXT("Can't call Enqueue(const TRequest&), invalid thread. Expected: %u, got: %u"), ResponsesProducerID, ThreadID());
return Requests.Enqueue(Item);
}
bool Enqueue(const TResponse& Item)
{
UE_CLOG(ThreadID() != ResponsesProducerID, LogTemp, Fatal, TEXT("Can't call Enqueue(const TResponse&), invalid thread. Expected: %u, got: %u"), RequestsProducerID, ThreadID());
return Responses.Enqueue(Item);
}
bool Enqueue(TRequest&& Item)
{
UE_CLOG(ThreadID() != RequestsProducerID, LogTemp, Fatal, TEXT("Can't call Enqueue(const TRequest&), invalid thread. Expected: %u, got: %u"), ResponsesProducerID, ThreadID());
return Requests.Enqueue(Item);
}
bool Enqueue(TResponse&& Item)
{
UE_CLOG(ThreadID() != ResponsesProducerID, LogTemp, Fatal, TEXT("Can't call Enqueue(const TResponse&), invalid thread. Expected: %u, got: %u"), RequestsProducerID, ThreadID());
return Responses.Enqueue(Item);
}
// Dequeue
bool Dequeue(TRequest& OutItem)
{
UE_CLOG(ThreadID() != ResponsesProducerID, LogTemp, Fatal, TEXT("Can't call Dequeue(TRequest& OutItem), invalid thread. Expected: %u, got: %u"), ResponsesProducerID, ThreadID());
return Requests.Dequeue(OutItem);
}
bool Dequeue(TResponse& OutItem)
{
UE_CLOG(ThreadID() != RequestsProducerID, LogTemp, Fatal, TEXT("Can't call Dequeue(TResponse& OutItem), invalid thread. Expected: %u, got: %u"), RequestsProducerID, ThreadID());
return Responses.Dequeue(OutItem);
}
// Is Empty?
bool IsEmpty() const
{
const uint32 Id = ThreadID();
if (Id == RequestsProducerID)
return Responses.IsEmpty();
else if (Id == ResponsesProducerID)
return Requests.IsEmpty();
else
{
UE_LOG(LogTemp, Fatal, TEXT("Can't call IsEmpty(), from an unknown thread: %d, RequestsProducerID: %u, ResponsesProducerID: %u"), Id, RequestsProducerID, ResponsesProducerID);
return false;
}
}
private:
TQueue<TRequest> Requests;
TQueue<TResponse> Responses;
volatile uint32 RequestsProducerID;
volatile uint32 ResponsesProducerID;
};
template<class TRequest, class TResponse>
TConduit<TRequest, TResponse>::~TConduit()
{
}
================================================
FILE: Source/InfraworldRuntime/Public/GenUtils.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "CoreMinimal.h"
#include "GenUtils.generated.h"
// XX - major version
// YY - minor version
// ZZ - patch
#define INFRAWORLD_RUNTIME_VERSION 010000
/**
* A wrapper for a TArray<uint8> used to avoid inner generics.
*/
USTRUCT(BlueprintType)
struct INFRAWORLDRUNTIME_API FByteArray
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadWrite, Category=ArrayWrapper)
TArray<uint8> Bytes;
FByteArray()
{
}
FByteArray(const TArray<uint8> &InBytes) :
Bytes(InBytes)
{
}
};
// ~~~~~ Network CONTEXT ~~~~~
/**
* The various compression algorithms supported by gRPC.
*/
UENUM(BlueprintType)
enum class EGrpcCompressionAlgorithm : uint8
{
CompressNone,
CompressDeflate,
CompressGzip
};
/**
* A ClientContext allows the person implementing a service client to:
*
* @todo Implement census/context if necessary.
*
* - Add custom metadata key-value pairs that will propagated to the server side.
* - Control call settings such as compression and authentication.
* - Initial and trailing metadata coming from the server.
* - Get performance metrics (ie, census).
*
* Context settings are only relevant to the call they are invoked with, that
* is to say, they aren't sticky. Some of these settings, such as the
* compression options, can be made persistent at channel construction time
* @see grpc::CreateCustomChannel()
*
* @warning ClientContext instances should not be reused across rpcs.
* @see https://grpc.io/grpc/cpp/classgrpc_1_1_client_context.html#details
*/
USTRUCT(BlueprintType)
struct INFRAWORLDRUNTIME_API FGrpcClientContext
{
GENERATED_USTRUCT_BODY()
/**
* Whether the optional Metadata attribute of the context is set.
*/
UPROPERTY(EditAnywhere, meta=(InlineEditConditionToggle), Category=Metadata)
bool bOverride_Metadata = false;
/**
* Note: Toggle an 'eye' if you DON'T NEED metadata at all.
*
* Add the (Key -> Value) pair to the metadata associated with a client call. These are made available at the server
* side by the grpc::ServerContext::client_metadata() method.
*
* Restrictions:
* - Metadata keys can't be empty.
* - Metadata keys can't start with ':'.
*
* Note: This method should only be called before invoking the rpc.
* Note: Keys are metadata keys. If meta_value is binary data, it must end in "-bin".
* Note: Value The metadata value. If its value is binary, it must be base64-encoding
* https://tools.ietf.org/html/rfc4648#section-4 and Key must end in "-bin".
*/
UPROPERTY(BlueprintReadWrite, meta=(editcondition=bOverride_Metadata), Category=Metadata)
TMap<FString, FString> Metadata;
/**
* Set the deadline for the client call. Won't apply any deadline if values are (-INF, 0].
* @warning This method should only be called before invoking the rpc.
*/
UPROPERTY(BlueprintReadWrite, Category=Metadata)
float DeadlineSeconds = -1.0f;
/**
* Set the per call authority header.
* @see https://tools.ietf.org/html/rfc7540#section-8.1.2.3.
*/
UPROPERTY(BlueprintReadWrite, Category=Metadata)
FString Authority;
/**
* Set an algorithm to be the compression algorithm used for the client call.
*/
UPROPERTY(BlueprintReadWrite, AdvancedDisplay, Category=Metadata)
EGrpcCompressionAlgorithm GrpcCompressionAlgorithm;
/**
* EXPERIMENTAL: Set this request to be idempotent.
*/
UPROPERTY(BlueprintReadWrite, AdvancedDisplay, Category=Metadata)
bool bIdempotent;
/**
* EXPERIMENTAL: Set this request to be cacheable.
*/
UPROPERTY(BlueprintReadWrite, AdvancedDisplay, Category=Metadata)
bool bCacheable;
/**
* EXPERIMENTAL: Trigger wait-for-ready or not on this request.
*/
UPROPERTY(BlueprintReadWrite, AdvancedDisplay, Category=Metadata)
bool bWaitForReady;
/**
* Flag whether the initial metadata should be corked. If corked is true, then the initial metadata will be colasced
* with the write of first message in the stream.
*/
UPROPERTY(BlueprintReadWrite, AdvancedDisplay, Category=Metadata)
bool bInitialMetadataCorked;
};
// ~~~~~ Wrappers for CONTEXT and STATUS ~~~~~
template<class TRequestType>
struct TRequestWithContext
{
TRequestType Request;
FGrpcClientContext Context;
TRequestWithContext()
{
}
TRequestWithContext(const TRequestType& InRequest, const FGrpcClientContext& InContext) :
Request(InRequest),
Context(InContext)
{
}
};
// Special constructor, automatically inferring arguments.
template<class T>
TRequestWithContext<T> TRequestWithContext$New(const T& InRequest, const FGrpcClientContext& InContext)
{
return TRequestWithContext<T>(InRequest, InContext);
}
/**
* GRPC error code.
*
* @see https://grpc.io/grpc/cpp/namespacegrpc.html#aff1730578c90160528f6a8d67ef5c43b
*/
UENUM(BlueprintType)
enum class EGrpcStatusCode : uint8
{
/**
* Not an error, returned on success.
*/
Ok = 0,
/**
* The operation was cancelled (typically by the caller).
*/
Cancelled = 1,
/**
* Unknown error. An example of where this error may be returned is if a
* Status value received from another address space belongs to an error-space
* that is not known in this address space. Also errors raised by APIs that
* do not return enough error information may be converted to this error.
*/
Unknown = 2,
/**
* Client specified an invalid argument. Note that this differs from
* FailedPrecondition. InvalidArgument indicates arguments that are
* problematic regardless of the state of the system (e.g., a malformed file
* name).
*/
InvalidArgument = 3,
/**
* Deadline expired before operation could complete. For operations that
* change the state of the system, this error may be returned even if the
* operation has completed successfully. For example, a successful response
* from a server could have been delayed long enough for the deadline to
* expire.
*/
DeadlineExceeded = 4,
/**
* Some requested entity (e.g., file or directory) was not found.
*/
NotFound = 5,
/**
* Some entity that we attempted to create (e.g., file or directory) already
* exists.
*/
AlreadyExists = 6,
/**
* The caller does not have permission to execute the specified operation.
* PermissionDenied must not be used for rejections caused by exhausting
* some resource (use ResourceExhausted instead for those errors).
* PermissionDenied must not be used if the caller can not be identified
* (use Unauthenticated instead for those errors).
*/
PermissionDenied = 7,
/**
* The request does not have valid authentication credentials for the
* operation.
*/
Unauthenticated = 16,
/**
* Some resource has been exhausted, perhaps a per-user quota, or perhaps the
* entire file system is out of space.
*/
ResourceExhausted = 8,
/**
* Operation was rejected because the system is not in a state required for
* the operation's execution. For example, directory to be deleted may be
* non-empty, an rmdir operation is applied to a non-directory, etc.
*
* A litmus test that may help a service implementor in deciding
* between FailedPrecondition, Aborted, and Unavailable:
* (a) Use Unavailable if the client can retry just the failing call.
* (b) Use Aborted if the client should retry at a higher-level
* (e.g., restarting a read-modify-write sequence).
* (c) Use FailedPrecondition if the client should not retry until
* the system state has been explicitly fixed. E.g., if an "rmdir"
* fails because the directory is non-empty, FailedPrecondition
* should be returned since the client should not retry unless
* they have first fixed up the directory by deleting files from it.
* (d) Use FailedPrecondition if the client performs conditional
* REST Get/Update/Delete on a resource and the resource on the
* server does not match the condition. E.g., conflicting
* read-modify-write on the same resource.
*/
FailedPrecondition = 9,
/**
* The operation was aborted, typically due to a concurrency issue l *
* sequencer check failures, transaction aborts, etc.
*
* See litmus test above for deciding between FailedPrecondition, Aborted,
* and Unavailable.
*/
Aborted = 10,
/**
* Operation was attempted past the valid range. E.g., seeking or reading
* past end of file.
*
* Unlike InvalidArgument, this error indicates a problem that may be fixed
* if the system state changes. For example, a 32-bit file system will
* generate InvalidArgument if asked to read at an offset that is not in the
* range [0,2^32-1], but it will generate OutOfRange if asked to read from
* an offset past the current file size.
*
* There is a fair bit of overlap between FailedPrecondition and
* OutOfRange. We recommend using OutOfRange (the more specific error)
* when it applies so that callers who are iterating through a space can
* easily look for an OutOfRange error to detect when they are done.
*/
OutOfRange = 11,
/**
* Operation is not implemented or not supported/enabled in this service.
*/
Unimplemented = 12,
/**
* Internal errors. Means some invariants expected by underlying System has
* been broken. If you see one of these errors, Something is very broken.
*/
Internal = 13,
/**
* The service is currently unavailable. This is a most likely a transient
* condition and may be corrected by retrying with a backoff.
*/
/**
* See litmus test above for deciding between FailedPrecondition, Aborted,
* and Unavailable.
*/
Unavailable = 14,
/**
* Unrecoverable data loss or corruption.
*/
DataLoss = 15
};
/**
* Did it work? If it didn't, why?
*
* @see https://grpc.io/grpc/cpp/classgrpc_1_1_status.html#details
*/
USTRUCT(BlueprintType)
struct INFRAWORLDRUNTIME_API FGrpcStatus
{
GENERATED_USTRUCT_BODY()
/**
* Return the instance's error code.
*/
UPROPERTY(BlueprintReadOnly, Category=Status)
EGrpcStatusCode ErrorCode;
/**
* Return the instance's error message.
*/
UPROPERTY(BlueprintReadOnly, Category=Status)
FString ErrorMessage;
/**
* The (binary) error details. Usually it contains a serialized google.rpc.Status proto.
*/
UPROPERTY(BlueprintReadOnly, Category=Status)
FString ErrorDetails;
};
template<class TResponseType>
struct TResponseWithStatus
{
TResponseType Response;
FGrpcStatus Status;
TResponseWithStatus()
{
}
TResponseWithStatus(const TResponseType& InResponse, const FGrpcStatus& InStatus) :
Response(InResponse),
Status(InStatus)
{
}
};
================================================
FILE: Source/InfraworldRuntime/Public/GrpcIncludesBegin.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
// Disable some warnings in GRPC
#if PLATFORM_WINDOWS
#pragma warning(push)
#pragma warning (disable : 4125)// decimal digit terminates...
#pragma warning (disable : 4647)// behavior change __is_pod...
#pragma warning (disable : 4668)// 'symbol' is not defined as a preprocessor macro...
#pragma warning (disable : 4456)// declaration of 'size' hides previous local declaration
#pragma warning (disable : 4577)// 'noexcept' used with no exception handling mode specified
#pragma warning (disable : 4946)// reinterpret_cast used between related classes
#pragma warning (disable : 4005)// 'TEXT': macro redefinition
#pragma warning (disable : 4582)// constructor is not implicitly called
#pragma warning (disable : 4583)// destructor is not implicitly called
#pragma warning (disable : 4800)// Implicit conversion from 'type' to bool. Possible information loss
#ifdef WINDOWS_PLATFORM_TYPES_GUARD
#pragma warning(push)
#include "Windows/HideWindowsPlatformTypes.h"
#endif
#elif PLATFORM_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundef"
#pragma clang diagnostic ignored "-Wshadow"
#endif
================================================
FILE: Source/InfraworldRuntime/Public/GrpcIncludesEnd.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
// Since GRPC actively uses winapi, we need to forbid windows macros
// (such as GetMessage, MemoryBarrier, etc.) in our remaining code.
// To do it, we 'wrap' all the C++ file's including ANY GRPC header files
// content into Allow/Hide WindowsPlatformTypes.
// We're unable to 'isolate' the WinAPI usage within a single C++ file thanks to Unity Build.
#if PLATFORM_WINDOWS
#pragma warning(pop)
#ifndef UE4_MINIMAL_WINDOWS_INCLUDE
#define UE4_MINIMAL_WINDOWS_INCLUDE
#endif
#include "Windows/AllowWindowsPlatformTypes.h"
#elif PLATFORM_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
================================================
FILE: Source/InfraworldRuntime/Public/GrpcUriValidator.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "CoreMinimal.h"
class FGrpcUriValidator
{
public:
/**
* Attempts to validate a URI, further provided to grpc::CreateChannel function.
* It does not tries to establish any kind of connections, so it checks only format.
*
* @param MaybeGrpcUri Grpc URI to validate.
* @param OutError Error message if any.
*
* @return True if the URI is valid and thus could be used, false otherwise.
*/
static bool Validate(const FString& MaybeGrpcUri, FString& OutError);
};
================================================
FILE: Source/InfraworldRuntime/Public/InfraworldRuntime.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "Modules/ModuleManager.h"
DECLARE_LOG_CATEGORY_EXTERN(LogInfraworldRuntime, Log, All);
class FInfraworldRuntimeModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
================================================
FILE: Source/InfraworldRuntime/Public/RpcClient.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Containers/Queue.h"
#include "Templates/SubclassOf.h"
#include "Templates/Atomic.h"
#include "RpcClientWorker.h"
#include "ChannelCredentials.h"
#include "RpcClient.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRpcErrorSignature, URpcClient*, Dispatcher, const FRpcError&, Error);
/**
* An RPC client used to interact with GRPC services from Blueprints and UE-compatible C++ code.
* Being used as a base class for generated RPC clients, an RPC Client contains a set of methods to manage an RPC Channel.
*
* @note You should never instantiate it directly.
*
* You should use a Proto2Cpp converter to create GRPC wrappers for an every single service.
*/
UCLASS(Abstract, BlueprintType, Blueprintable)
class INFRAWORLDRUNTIME_API URpcClient : public UObject
{
GENERATED_BODY()
bool Init(const FString& URI, UChannelCredentials* ChannelCredentials);
public:
URpcClient();
virtual ~URpcClient();
// Being called in implementations
virtual void HierarchicalInit() PURE_VIRTUAL(URpcClient::HierarchicalInit,);
// Being called in implementations
virtual void HierarchicalUpdate() PURE_VIRTUAL(URpcClient::HierarchicalUpdate,);
/**
* Stops and disables this instance of RPC Client.
* This operation is irreversible, you can't either send or receive requests and responses after doing so.
*
* @param bSynchronous
* Stop and wait a sec. Oh when you look at me like that my darling - What did you expect?
* If seriously - you should think thousand times before unchecking this option, because
* you can experience heavy and unpredictable racing hazard.
*/
UFUNCTION(BlueprintCallable, Category="Vizor|RPC Client")
void Stop(bool bSynchronous = true);
/**
* An update function, used to tell a Client, when to check queries and dispatch messages.
* In widget (for example) you should call it every update (or redraw/invalidate).
*
* @note that frequency, you're calling this method won't ever affect the speed of message processing.
* @deprecated No need to call this function anymore.
*/
UFUNCTION(BlueprintCallable, Category = "Vizor|RPC Client", meta = (DeprecatedFunction, DeprecationMessage = "No need to call this function anymore: Updates now are being dispatched automatically"))
void Update();
/**
* Checks whether the RPC Client could send requests.
*
* @return True if the RPC Client is properly initialized and can send requests. If not - all requests will be ignored.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Vizor|RPC Client", meta=(DisplayName="Can Send Requests?"))
bool CanSendRequests() const;
/**
* Instantiates a new RPC Dispatcher. You should use this function, not 'Construct Object from Class', to properly initialize the instance.
*
* @param Class
* A subclass of RPC Client, that will be instantiated. Please don't select an abstract 'Rpc Client'.
* @param InstantiationParameters
* Parameters, will be used for instantiation,
* @param Outer
* An outer object, None (or empty) is valid as well, if so, GetTransientPackage() will be used to retrieve a static outer.
* @return A newly created instance of RPC Client.
*/
UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category="Vizor|RPC Client", meta=(DisplayName="Create RPC Client", DeterminesOutputType="Class", DeprecatedFunction, DeprecationMessage="Use function, accepting URI and ChannelCredentials instead"))
static URpcClient* CreateRpcClient(TSubclassOf<URpcClient> Class, FRpcClientInstantiationParameters InstantiationParameters, UObject* Outer = nullptr);
/**
* Instantiates a new RPC Dispatcher. You should use this function, not 'Construct Object from Class', to properly initialize the instance.
*
* @param Class
* A subclass of RPC Client, that will be instantiated. Please don't select an abstract 'Rpc Client'.
* @param URI
* Endpoint URI, will be used to establish connection.
* Must not start with 'http://' or 'https://' or any URL scheme.
* Must not have URL path, such as 'www.hello.eu/paths/are/not/allowed'
* Must be either a domain name or an IP address with or without an explicit port. 80'th port is used by default.
* @param ChannelCredentials
* Credentials to use for the created RPC client.
* @param Outer
* An outer object, None (or empty) is valid as well, if so, GetTransientPackage() will be used to retrieve a static outer.
* @return A newly created instance of RPC Client.
*/
UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category="Vizor|RPC Client", meta=(DisplayName="Create RPC Client", DeterminesOutputType="Class"))
static URpcClient* CreateRpcClientUri(TSubclassOf<URpcClient> Class, const FString& URI, UChannelCredentials* ChannelCredentials, UObject* Outer = nullptr);
/**
* Called when did received any kind of error.
*/
UPROPERTY(BlueprintAssignable, Category="Vizor|RPC Client", meta=(DisplayName="Event RPC Error"))
FRpcErrorSignature EventRpcError;
protected:
/** A pointer to an inner RpcClientWorker sending and receiving messages */
TUniquePtr<RpcClientWorker> InnerWorker;
private:
virtual void BeginDestroy() override;
/** Whether the RPC Client could send requests or not */
bool bCanSendRequests = false;
/** A thread, where RPC client worker will reside */
TAtomic<FRunnableThread*> Thread = { nullptr };
/** An accumulator for error messages */
TQueue<FRpcError> ErrorMessageQueue;
/**
* Global engine ticker handler
* Only "IsValid() -> true" if this RPC client "CanSendRequests() -> true"
*/
FDelegateHandle TickDelegateHandle;
};
template <class T>
FORCEINLINE T* NewRpcClient(const FString& URI, UChannelCredentials* ChannelCredentials, UObject* Outer = nullptr)
{
static_assert(TIsDerivedFrom<T, URpcClient>::IsDerived, "T must derive URpcClient");
static_assert(!TIsSame<T, URpcClient>::Value, "T must derive URpcClient, but mustn't be a bare URpcClient");
return Cast<T>(URpcClient::CreateRpcClientUri(T::StaticClass(), URI, ChannelCredentials, Outer));
}
================================================
FILE: Source/InfraworldRuntime/Public/RpcClientWorker.h
================================================
/*
* Copyright 2018 Vizor Games LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "CoreMinimal.h"
#include "Containers/Queue.h"
#include "ChannelCredentials.h"
#include "HAL/Runnable.h"
#include "InfraworldRuntime.h"
#include <memory>
#include <chrono>
#include "Templates/Atomic.h"
enum class ERpcWorkerState : uint8
{
PendingInitialization,
Initializing,
Working,
PendingShutdown,
Shutdown
};
class FGenAsyncRequest;
/**
* Base RPC Client Worker, it 'lives' in a separate thread and updates all conduits with responses.
*/
class INFRAWORLDRUNTIME_API RpcClientWorker : public FRunnable
{
public:
RpcClientWorker();
virtual ~RpcClientWorker();
virtual uint32 Run() override;
FORCEINLINE bool IsPendingStopped() const
{
return WorkerState.Load() == ERpcWorkerState::PendingShutdown;
}
FORCEINLINE void MarkPendingStopped()
{
UE_LOG(LogInfraworldRuntime, Log, TEXT("RpcClientWorker at [%p] Marking pending stopped"), this);
const ERpcWorkerState PreviousWorkerState = WorkerState.Exchange(ERpcWorkerState::PendingShutdown);
static const TSet<ERpcWorkerState> ExpectedWorkerStates = {
ERpcWorkerState::PendingInitialization, ERpcWorkerState::Initializing, ERpcWorkerState::Working
};
ensureAlways(ExpectedWorkerStates.Contains(PreviousWorkerState));
}
virtual bool HierarchicalInit() = 0;
virtual void HierarchicalUpdate() = 0;
void DispatchError(const FString& ErrorMessage);
//public:
FString URI;
UChannelCredentials* ChannelCredentials;
TQueue<FRpcError>* ErrorMessageQueue;
protected:
TAtomic<ERpcWorkerState> WorkerState;
};
================================================
FILE: Source/InfraworldRuntime/Public/WorkerUtils.h
================================================
#pragma once
#include "GenUtils.h"
#include "CastUtils.h"
#include "Templates/Invoke.h"
#include "RpcClientWorker.h"
#include "GrpcIncludesBegin.h"
#include "ChannelProvider.h"
#include <grpc/support/log.h>
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/completion_queue.h>
#include <grpcpp/impl/codegen/async_unary_call.h>
#include "GrpcIncludesEnd.h"
template <class TStub>
class TStubbedRpcWorker : public RpcClientWorker
{
public:
template <class TUnrealRequest, class TProtoRequest, class TUnrealResponse, class TProtoResponse, class TStubRequestFunctionPointer>
TResponseWithStatus<TUnrealResponse> AsyncRequest(const TUnrealRequest Request, const FGrpcClientContext Context, const TStubRequestFunctionPointer MemberPointer)
{
const TProtoRequest ClientRequest = casts::Proto_Cast<TProtoRequest>(Request);
grpc::ClientContext ClientContext;
casts::CastClientContext(Context, ClientContext);
grpc::CompletionQueue Queue;
grpc::Status Status;
std::unique_ptr<grpc::ClientAsyncResponseReader<TProtoResponse>> Rpc(Invoke(MemberPointer, Stub.get(), &ClientContext, ClientRequest, &Queue));
TProtoResponse Response;
Rpc->Finish(&Response, &Status, (void*)1);
void* got_tag;
bool ok = false;
while (true)
{
const std::chrono::seconds SingleWaitDuration = std::chrono::seconds(1);
const std::chrono::time_point<system_clock> Deadline = std::chrono::system_clock::now() +
SingleWaitDuration;
const grpc::CompletionQueue::NextStatus NextStatus = Queue.AsyncNext(&got_tag, &ok, Deadline);
if (NextStatus == grpc::CompletionQueue::NextStatus::GOT_EVENT)
{
break;
}
if (IsPendingStopped())
{
ClientContext.TryCancel();
}
}
GPR_ASSERT(got_tag == (void*)1);
GPR_ASSERT(ok);
FGrpcStatus GrpcStatus;
casts::CastStatus(Status, GrpcStatus);
TResponseWithStatus<TUnrealResponse> Result(casts::Proto_Cast<TUnrealResponse>(Response), GrpcStatus);
return Result;
}
protected:
std::unique_ptr<TStub> Stub;
};
gitextract_0btcdab7/
├── .gitignore
├── InfraworldRuntime.uplugin
├── LICENSE
├── README.md
├── Setup.bat
├── Setup.command
├── Setup.sh
└── Source/
└── InfraworldRuntime/
├── InfraWorldRuntime.Build.cs
├── Private/
│ ├── ChannelCredentials.cpp
│ ├── GrpcUriValidator.cpp
│ ├── InfraworldRuntime.cpp
│ ├── RpcClient.cpp
│ └── RpcClientWorker.cpp
└── Public/
├── CastUtils.h
├── ChannelCredentials.h
├── ChannelProvider.h
├── Conduit.h
├── GenUtils.h
├── GrpcIncludesBegin.h
├── GrpcIncludesEnd.h
├── GrpcUriValidator.h
├── InfraworldRuntime.h
├── RpcClient.h
├── RpcClientWorker.h
└── WorkerUtils.h
SYMBOL INDEX (39 symbols across 14 files)
FILE: Source/InfraworldRuntime/InfraWorldRuntime.Build.cs
class InfraworldRuntime (line 28) | public class InfraworldRuntime : ModuleRules
class ModuleDepPaths (line 40) | public class ModuleDepPaths
method ModuleDepPaths (line 45) | public ModuleDepPaths(string[] headerPaths, string[] libraryPaths)
method ToString (line 51) | public override string ToString()
method clog (line 57) | [Conditional("DEBUG")]
method FindFilesInDirectory (line 64) | private IEnumerable<string> FindFilesInDirectory(string dir, string su...
method GetConfigurationString (line 82) | private string GetConfigurationString()
method GatherDeps (line 87) | public ModuleDepPaths GatherDeps()
method InfraworldRuntime (line 121) | public InfraworldRuntime(ReadOnlyTargetRules Target) : base(Target)
FILE: Source/InfraworldRuntime/Private/ChannelCredentials.cpp
function UChannelCredentials (line 25) | UChannelCredentials* UChannelCredentials::MakeGoogleDefaultCredentials()
function UChannelCredentials (line 30) | UChannelCredentials* UChannelCredentials::MakeSslCredentials(FString Pem...
function UChannelCredentials (line 41) | UChannelCredentials* UChannelCredentials::MakeInsecureChannelCredentials()
FILE: Source/InfraworldRuntime/Private/GrpcUriValidator.cpp
class FGrpcUriValidator_Internal (line 21) | class FGrpcUriValidator_Internal
FILE: Source/InfraworldRuntime/Private/RpcClient.cpp
function URpcClient (line 123) | URpcClient* URpcClient::CreateRpcClient(TSubclassOf<URpcClient> Class, F...
function URpcClient (line 129) | URpcClient* URpcClient::CreateRpcClientUri(TSubclassOf<URpcClient> Class...
FILE: Source/InfraworldRuntime/Private/RpcClientWorker.cpp
function uint32 (line 41) | uint32 RpcClientWorker::Run()
FILE: Source/InfraworldRuntime/Public/CastUtils.h
function namespace (line 39) | namespace casts
function FORCEINLINE (line 223) | FORCEINLINE void CastClientContext(const FGrpcClientContext &InContext, ...
FILE: Source/InfraworldRuntime/Public/ChannelCredentials.h
function INFRAWORLDRUNTIME_API (line 31) | INFRAWORLDRUNTIME_API FRpcError
function INFRAWORLDRUNTIME_API (line 47) | INFRAWORLDRUNTIME_API UChannelCredentials : public UObject
function INFRAWORLDRUNTIME_API (line 118) | INFRAWORLDRUNTIME_API USslCredentials : public UChannelCredentials
function INFRAWORLDRUNTIME_API (line 148) | INFRAWORLDRUNTIME_API FRpcClientInstantiationParameters
FILE: Source/InfraworldRuntime/Public/ChannelProvider.h
function namespace (line 25) | namespace channel
FILE: Source/InfraworldRuntime/Public/Conduit.h
function AcquireRequestsProducer (line 47) | void AcquireRequestsProducer()
function AcquireResponsesProducer (line 59) | void AcquireResponsesProducer()
function Enqueue (line 65) | bool Enqueue(const TRequest& Item)
function Enqueue (line 71) | bool Enqueue(const TResponse& Item)
function Enqueue (line 77) | bool Enqueue(TRequest&& Item)
function Enqueue (line 83) | bool Enqueue(TResponse&& Item)
function Dequeue (line 90) | bool Dequeue(TRequest& OutItem)
function Dequeue (line 96) | bool Dequeue(TResponse& OutItem)
function IsEmpty (line 103) | bool IsEmpty() const
FILE: Source/InfraworldRuntime/Public/GenUtils.h
function INFRAWORLDRUNTIME_API (line 30) | INFRAWORLDRUNTIME_API FByteArray
function INFRAWORLDRUNTIME_API (line 79) | INFRAWORLDRUNTIME_API FGrpcClientContext
function INFRAWORLDRUNTIME_API (line 337) | INFRAWORLDRUNTIME_API FGrpcStatus
FILE: Source/InfraworldRuntime/Public/GrpcUriValidator.h
function class (line 20) | class FGrpcUriValidator
FILE: Source/InfraworldRuntime/Public/InfraworldRuntime.h
function class (line 22) | class FInfraworldRuntimeModule : public IModuleInterface
FILE: Source/InfraworldRuntime/Public/RpcClient.h
function INFRAWORLDRUNTIME_API (line 43) | INFRAWORLDRUNTIME_API URpcClient : public UObject
FILE: Source/InfraworldRuntime/Public/RpcClientWorker.h
function ERpcWorkerState (line 29) | enum class ERpcWorkerState : uint8
Condensed preview — 25 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (122K chars).
[
{
"path": ".gitignore",
"chars": 1198,
"preview": "\n# Created by https://www.gitignore.io/api/linux,macos,windows\n\n### Linux ###\n*~\n\n# temporary files which can be created"
},
{
"path": "InfraworldRuntime.uplugin",
"chars": 664,
"preview": "{\n\t\"FileVersion\": 3,\n\t\"Version\": 1,\n\t\"VersionName\": \"1.0\",\n\t\"FriendlyName\": \"Infraworld Runtime\",\n\t\"Description\": \"Infra"
},
{
"path": "LICENSE",
"chars": 11345,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 5272,
"preview": "Vizor Infraworld\n================\n\n[](https://open"
},
{
"path": "Setup.bat",
"chars": 4622,
"preview": "@echo off\n\n::#####################################VARS##################################################################"
},
{
"path": "Setup.command",
"chars": 6410,
"preview": "#!/bin/bash\n\n# Exit on errors if any\nset -e\n\n###########################################################################"
},
{
"path": "Setup.sh",
"chars": 8003,
"preview": "#!/bin/bash\n\n# Exit on errors if any\nset -e\n\n###########################################################################"
},
{
"path": "Source/InfraworldRuntime/InfraWorldRuntime.Build.cs",
"chars": 5043,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Private/ChannelCredentials.cpp",
"chars": 1483,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Private/GrpcUriValidator.cpp",
"chars": 6737,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Private/InfraworldRuntime.cpp",
"chars": 1220,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Private/RpcClient.cpp",
"chars": 6476,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Private/RpcClientWorker.cpp",
"chars": 2571,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/CastUtils.h",
"chars": 10844,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/ChannelCredentials.h",
"chars": 6131,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/ChannelProvider.h",
"chars": 4686,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "Source/InfraworldRuntime/Public/Conduit.h",
"chars": 4498,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/GenUtils.h",
"chars": 11760,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/GrpcIncludesBegin.h",
"chars": 1740,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/GrpcIncludesEnd.h",
"chars": 1190,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/GrpcUriValidator.h",
"chars": 1124,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/InfraworldRuntime.h",
"chars": 898,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/RpcClient.h",
"chars": 7008,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/RpcClientWorker.h",
"chars": 2166,
"preview": "/*\n * Copyright 2018 Vizor Games LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "Source/InfraworldRuntime/Public/WorkerUtils.h",
"chars": 2088,
"preview": "#pragma once\n\n#include \"GenUtils.h\"\n#include \"CastUtils.h\"\n#include \"Templates/Invoke.h\"\n#include \"RpcClientWorker.h\"\n\n#"
}
]
About this extraction
This page contains the full source code of the vizor-games/InfraworldRuntime GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 25 files (112.5 KB), approximately 28.8k tokens, and a symbol index with 39 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.