Search code examples
c++serializationprotocol-buffersunreal-engine5

Protoc failed to parse input if integer value more than 127 in Unreal Engine 5 c++


Environment:

  • Unreal Engine 5
  • Windows 10
  • Protocol Buffers v3.18.0

I'm trying to decode serialized data in Unreal Engine 5 (c++) by using protoc. If the message contains the value of int var less than 127 everything is okay. But if the value more than 127 I catch the error: Failed to parse input.

player.proto:

syntax = "proto3";
package com.game;

message Player {
    string username = 1;
    int64 experience = 2;
}

Success case

C++ serialization and save to file:

com::game::Player MyPlayer;
MyPlayer.set_username("test user name");
MyPlayer.set_experience(127); // <-- PAY ATTENTION

// serialization
std::string MyPlayerString;
if(!MyPlayer.SerializeToString(&MyPlayerString))
{
    UE_LOG(LogGameInstance, Error, TEXT("Can't serialize MyPlayer to String"));
    return;
}
const FString MyPlayerFString(MyPlayerString.c_str());

// save to file
const FString File = *FPaths::Combine(FPaths::GameSourceDir(), FApp::GetProjectName(), TEXT("temp.dat"));
FFileHelper::SaveStringToFile(
    MyPlayerFString,
    *File,
    FFileHelper::EEncodingOptions::AutoDetect,
    &IFileManager::Get()
);

protoc --decode_raw < temp.dat

1: "test user name"
2: 127

Fail case

C++ serialization and save to file:

...
MyPlayer.set_experience(128); // <-- PAY ATTENTION
...

protoc --decode_raw < temp.dat

Failed to parse input.

I guess the problem occurs when I try to convert std::string -> FString. Any ideas?


Solution

  • I found a solution.

    I'll keep it here, maybe it will help somebody. I changed the implementation to avoid string conversion.

    Before:

    ...
    
    // serialization
    std::string MyPlayerString;
    if(!MyPlayer.SerializeToString(&MyPlayerString))
    {
        UE_LOG(LogGameInstance, Error, TEXT("Can't serialize MyPlayer to String"));
        return;
    }
    const FString MyPlayerFString(MyPlayerString.c_str());
    
    // save to file
    const FString File = *FPaths::Combine(FPaths::GameSourceDir(), FApp::GetProjectName(), TEXT("temp.dat"));
    FFileHelper::SaveStringToFile(
        MyPlayerFString,
        *File,
        FFileHelper::EEncodingOptions::AutoDetect,
        &IFileManager::Get()
    );
    

    After:

    // serialization
    const auto MyPlayerSize = MyPlayer.ByteSizeLong();
    const auto MyPlayerBytes = new uint8[MyPlayerSize]();
    if(!MyPlayer.SerializeToArray(MyPlayerBytes, MyPlayerSize))
    {
        UE_LOG(LogGameInstance, Error, TEXT("Can't serialize MyPlayer to Array"));
        return;
    }
    TArray64<uint8>* MyPlayerArray = new TArray<uint8, FDefaultAllocator64>(MyPlayerBytes, MyPlayerSize);
    
    // save to file
    const FString File = *FPaths::Combine(FPaths::GameSourceDir(), FApp::GetProjectName(), TEXT("temp.dat"));
    FFileHelper::SaveArrayToFile(
        *MyPlayerArray,
        *File,
        &IFileManager::Get()
    );