这里有一篇现成可用的教程
UE4随笔:接入 Google Protobuf 库
需要注意的点:
Protobuf和Protoc的版本要一样,Protoc可以不用按照上面教程中自己生成解决方案(只生成libprotobuf就可以),直接从git下载然后配置环境变量就可以
这个方法不适用 3.22之后的版本,因为下载的文件内容差别比较大,以最新的3.23.4为例
(1)3.22之后不再有单个语言的版本,需要下载完整版
(2)完整版的CMakeList是在根目录下,不在cmake文件夹中,所以cmake的时候src要改成根目录
(3)新版本的Protobuf不再自带googletest和abseil,需要单独下载到third_party下对应的文件夹里
·
(4)cmake的时候可能会报缺少zlib,就需要再下载zlib,然后cmake并生成库文件(操作和构建protobuf完全一样),再将生成的Debug和Release库路径添加到当前cmake的配置选项中,具体可以看这篇文章的前半部分
(5)cmake完生成的文件夹中不再有extract_include.bat文件,无法提取头文件库,lib的生成按照上述步骤是可以正常生成的
上面的文章中是将Protobuf构建为了第三方库,如果要构建为插件的话,build.cs应该这么配置,注意include和lib文件夹路径和实际路径匹配就可以了
public Protobuf(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
//Type = ModuleType.External;
if (Target.Platform == UnrealTargetPlatform.Win64)
{
PublicSystemIncludePaths.Add(Path.Combine(ModuleDirectory, "include"));
PublicSystemIncludePaths.Add(Path.Combine(ModuleDirectory, "lib"));
PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "lib", "libprotobuf.lib"));
}
ShadowVariableWarningLevel = WarningLevel.Off;
bEnableUndefinedIdentifierWarnings = false;
bEnableExceptions = true;
PublicDefinitions.Add("_CRT_SECURE_NO_WARNINGS");
PublicDefinitions.Add("GOOGLE_PROTOBUF_NO_RTTI=1");
PublicDefinitions.Add("GOOGLE_PROTOBUF_CMAKE_BUILD");
}
bool SerializeToArray(
void* data,
int size) const
bool SerializeToString(
std::string* output) const
(function) ParseFromString: Any
以webscoket为例,unreal端的发送应该是这样,如果不将isBinary设置为true或者使用SerializeToString +Send(FString&)以字符串形式发送都会在python服务器端解析出错
char buf[2048];
ClientMsg.SerializeToArray(buf, ClientMsg.ByteSizeLong());
WebSocket->Send(Data, ClientMsg.ByteSizeLong(), true);
同理,因为protobuf消息为字节流形式,所以unreal端在接收时需要使用OnBinaryMessage而不是OnMessage,需要处理下一个包过大被分成多个Fragment的情况
WebSocket->OnBinaryMessage().AddRaw(this, &AIGameCoreNetwork::OnReceiveMessage);
void AIGameCoreNetwork::OnReceiveMessage(const void* Data, SIZE_T Size, bool bIsLastFragment)
{
//UE_LOG(LogAIGameCoreNetwork, Log, TEXT("[ReceiveMessage]: %s"), *ReceivedMsg);
if(ReceivedIndex + Size >= MAX_RECEIVE_BUFFER_SIZE)
{
UE_LOG(LogAIGameCoreNetwork, Error, TEXT("Receive Buffer runs out!"));
return;
}
memcpy(&ReceiveBuffer[ReceivedIndex], Data, Size);
ReceivedIndex += Size;
if(bIsLastFragment)
{
ReceiveAIServerMsgEvent.Broadcast(ReceiveBuffer, ReceivedIndex);
ReceivedIndex = 0;
}
}
message Msg{
enum MsgType{
A = 0;
B = 1;
}
MsgType MsgType = 1;
google.protobuf.Any MsgContent = 2;
}
FModuleManager::Get().LoadModuleChecked(TEXT("WebSockets"));
WebSocket = FWebSocketsModule::Get().CreateWebSocket(ServerURL, ServerProtocol);
self.__server = websockets.serve(self.__handler, self.__host, self.__port)
async def __handler(self, websocket):
self.__websocket = websocket
async for message in websocket:
await self.msglistener(message)
async def send(self, message):
await self.__websocket.send(message)
try:
await self.__websocket.send(msg)
except websockets.exceptions.ConnectionClosed:
self.__on_client_disconnect()
try:
async for message in websocket:
self.msglistener(message)
except websockets.exceptions.ConnectionClosed:
self.__on_client_disconnect()
在unreal客户端这边,主要需要注意下protobuf的解析为字节流形式的问题,以及websocket使用binarymessage进行发送和接收
python服务器不需要关心类型问题,注意下需要先收到客户端消息才能获取到对应的websocket,然后需要保存下来用于之后主动发消息就可以了。因为发消息本身是异步,如果直接由逻辑类型发送就需要大部分函数都是async,可以考虑逻辑类型仅将消息放进队列中,然后有一个独立的线程以一定时间间隔发送消息