Skip to content

Provides save data serialization functionality using Unreal Engine's UHT

License

Notifications You must be signed in to change notification settings

laycnc/UnrealSaveDataPipeline

Repository files navigation

This "README.md" was created by DeepL translation

This Plugin is under implementation.

Overview

This Plugin performs code generation for UnrealEngine save data.
UHT (UnrealHeaderTool) is used for code generation.

In the save mechanism using USaveGame, serialization is performed in a reflection-based manner.
The use of reflection results in performance degradation.
Additional information such as meta information is also saved, which increases the file size.
By generating the code in advance, the above disadvantages are eliminated.

For generated code

Description OutputConditions OutputFunctions
structure read function Always void SavePipelineRead(FMemoryReader& MemoryReader);
Structure write function Always void SavePipelineWrite(FMemoryWriter& MemoryWriter);
Structure conversion function between old version and new version Structure conversion function between old version and new version BaseType must be put in the meta-information of USTRUCT.

Input Example

Sample.cpp

// Older version of the structure
USTRUCT(meta = (SaveDataPipeline))
struct FSaveDataSampleStructOldVersion
{
	GENERATED_BODY()

    GENERATED_SAVE_PIPELINE_BODY()

public:
	UPROPERTY(EditAnywhere)
	int32 DeadCount;

	UPROPERTY(EditAnywhere)
	FString HogeHoge;
}

// new version structure
USTRUCT(meta = (SaveDataPipeline, BaseType = "SaveDataSampleStructOldVersion"))
struct FSaveDataSampleStruct
{
	GENERATED_BODY()

    GENERATED_SAVE_PIPELINE_BODY()

public:
	UPROPERTY(EditAnywhere)
	int32 DeadCount;

	UPROPERTY(EditAnywhere)
	FString HogeHoge;
}

Output code.

XXX.savepipeline.h works in the same way as the UE standard GENERATED_BODY macro.
By writing the dedicated macro GENERATED_SAVE_PIPELINE_BODY, the generated code functions can be included in the members.

Sample.savepipeline.h

#define FID_XXXXXX_Source_Example_Public_Sample_h__25_SAVE_PIPELINE_HASH \
public: \
static int32 GetSavePipelineHash()\
{\
	constexpr int32 Hash = -323057014;\
	return Hash;\
}\

#define FID_XXXXXX_Source_Example_Public_Sample_h_25_SAVE_PIPELINE_SERIALIZE \
public: \
void SavePipelineRead(FMemoryReader& MemoryReader, int32* InReadHash = nullptr);\
void SavePipelineWrite(FMemoryWriter& MemoryWriter);

#define FID_XXXXXX_Source_Example_Public_Sample_h__25_SAVE_PIPELINE_CONVERT \
public: \
void SavePipelineConvert(const FSaveDataSampleStructOldVersion& InPrevData);

#define FID_XXXXXX_Source_Example_Public_Sample_h__25_GENERATED_SAVE_PIPELINE_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
	FID_XXXXXX_Source_Example_Public_Sample_h__25_SAVE_PIPELINE_HASH \
	FID_XXXXXX_Source_Example_Public_Sample_h__25_SAVE_PIPELINE_SERIALIZE \
	FID_XXXXXX_Source_Example_Public_Sample_h__25_SAVE_PIPELINE_CONVERT \
PRAGMA_DISABLE_DEPRECATION_WARNINGS 

The output cpp code is serialized and converted by reflection.

  • Currently, only assignment is supported for conversion, but eventually enumeration conversion will be supported.

Sample.savepipeline.gen.cpp

// Function to convert structures between old and new versions.
bool FSaveDataSampleStruct::SavePipelineConvert(const FSaveDataSampleStructOldVersion& InPrevData)
{
	switch(InPrevData.Flag)
	{
		case ESaveDataFlag::HogeHoge:
			Flag = ESaveDataFlagOldVersion::HogeHoge;
			break;
		case ESaveDataFlag::Foo:
			Flag = ESaveDataFlagOldVersion::Foo;
			break;
		case ESaveDataFlag::Piyo:
			Flag = ESaveDataFlagOldVersion::Piyo;
			break;
		default:
			break;
	}

	Count = InPrevData.Count;
	HogeHoge = InPrevData.HogeHoge;
	Time = InPrevData.Time;
	// Success!
	return true;
}

// memory loading process
bool FSaveDataSampleStruct::SavePipelineRead(FMemoryReader& MemoryReader, int32* InReadHash)
{
	int32 Hash = 0;
	if( InReadHash == nullptr )
	{
		MemoryReader << Hash;
	}
	if( Hash != FSaveDataSampleStruct::GetSavePipelineHash() )
	{
		FSaveDataSampleStructOldVersion OldVersion;
		if( OldVersion.SavePipelineRead( MemoryReader, &Hash ) )
		{
			return SavePipelineConvert(OldVersion);
		}
		return false;
	}
	MemoryReader << Flag;
	MemoryReader << Count;
	MemoryReader << HogeHoge;
	MemoryReader << Time;
	// Success!
	return true;
}

// Memory write process
bool FSaveDataSampleStruct::SavePipelineWrite(FMemoryWriter& MemoryWriter)
{
	int32 Hash = FSaveDataSampleStruct::GetSavePipelineHash();
	MemoryWriter << Hash;
	MemoryWriter << Flag;
	MemoryWriter << Count;
	MemoryWriter << HogeHoge;
	MemoryWriter << Time;
	// Success!
	return true;
}

Save process

Sample load process

bool LoadExample( const FSaveDataSampleStruct& OutSaveData, const FString& SlotName, const int32 UserIndex )
{
	TArray<uint8> SaveDataBinary;
	if( UGameplayStatics::LoadDataFromSlot(SaveDataBinary, SlotName, UserIndex) )
	{
		FSaveDataSampleStruct SaveVersion1 = {};
		FMemoryReader MemoryReader(SaveDataBinary, true);
		if( SaveData.SavePipelineRead(MemoryReader) )
		{
			OutSaveData = SaveData;
			return true;
		}
	}
	return false;
}

Sample save process

bool SaveExample( const FSaveDataSampleStruct& SaveData, const FString& SlotName, const int32 UserIndex )
{
	TArray<uint8> SaveDataBinary;
	FMemoryWriter MemoryWriter(SaveDataBinary, true);
	if( SaveData.SavePipelineWrite( MemoryWriter ) )
	{
		if( UGameplayStatics::SaveDataToSlot(SaveDataBinary, SlotName, UserIndex) )
		{
			return true;
		}
	}
	return false;
}

About

Provides save data serialization functionality using Unreal Engine's UHT

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published