Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Source/SaveExtension/Private/LevelFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ bool FSELevelFilter::Stores(const AActor* Actor) const
return ActorFilter.IsAllowed(Actor->GetClass());
}

bool FSELevelFilter::StoresAnyComponents() const
{
return ComponentFilter.IsAnyAllowed();
}
bool FSELevelFilter::Stores(const UActorComponent* Component) const
{
return ComponentFilter.IsAllowed(Component->GetClass());
Expand Down
9 changes: 9 additions & 0 deletions Source/SaveExtension/Private/SaveSlot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ void USaveSlot::CaptureThumbnail(
}
}

USaveSlotData* USaveSlot::AssureData()
{
if (!IsValid(Data))
{
AssignData(NewObject<USaveSlotData>(this, DataClass, NAME_None));
}
return Data;
}

bool USaveSlot::ShouldDeserializeAsync() const
{
return MultithreadedSerialization == ESEAsyncMode::LoadAsync ||
Expand Down
34 changes: 34 additions & 0 deletions Source/SaveExtension/Private/SaveSlotData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,37 @@ void USaveSlotData::CleanRecords(bool bKeepSublevels)
SubLevels.Empty();
}
}

FPlayerRecord& USaveSlotData::FindOrAddPlayerRecord(const FUniqueNetIdRepl& UniqueId)
{
return Players[Players.AddUnique({UniqueId})];
}

FPlayerRecord* USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId)
{
const int32 Index = Players.IndexOfByPredicate([&UniqueId](const FPlayerRecord& Record) {
return Record.UniqueId == UniqueId;
});
if (Index != INDEX_NONE)
{
return &Players[Index];
}
return nullptr;
}

bool USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId, UPARAM(Ref) FPlayerRecord& Record)
{
if (FPlayerRecord* FoundRecord = FindPlayerRecord(UniqueId))
{
Record = *FoundRecord;
return true;
}
return false;
}

bool USaveSlotData::RemovePlayerRecord(const FUniqueNetIdRepl& UniqueId)
{
return Players.RemoveAll([&UniqueId](const FPlayerRecord& Record){
return Record.UniqueId == UniqueId;
}) > 0;
}
56 changes: 47 additions & 9 deletions Source/SaveExtension/Private/Serialization/Records.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
#include "Serialization/Records.h"

#include "SaveExtension.h"
#include "LevelFilter.h"
#include "ClassFilter.h"
#include "SaveSlotData.h"
#include "Serialization/SEArchive.h"

#include <GameFramework/PlayerState.h>
#include <GameFramework/PlayerController.h>
#include <GameFramework/Pawn.h>



/////////////////////////////////////////////////////
Expand Down Expand Up @@ -86,7 +90,7 @@ const FName SERecords::TagNoPhysics{"!SavePhysics"};
const FName SERecords::TagNoTags{"!SaveTags"};


void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFilter& Filter)
void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const FSEClassFilter& ComponentFilter)
{
TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor);

Expand Down Expand Up @@ -131,12 +135,12 @@ void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const
}
}

if (Filter.StoresAnyComponents())
if (ComponentFilter.IsAnyAllowed())
{
for (auto* Component : Actor->GetComponents())
{
TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor | Component);
if (IsValid(Component) && Filter.Stores(Component))
if (IsValid(Component) && ComponentFilter.IsAllowed(Component->GetClass()))
{
FComponentRecord& ComponentRecord = Record.ComponentRecords.Add_GetRef({Component});
if (const auto* SceneComp = Cast<USceneComponent>(Component))
Expand Down Expand Up @@ -170,7 +174,7 @@ void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const
const_cast<AActor*>(Actor)->Serialize(Archive);
}

bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter)
bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSEClassFilter& ComponentFilter)
{
TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeActor);

Expand Down Expand Up @@ -205,12 +209,12 @@ bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, cons

TRACE_CPUPROFILER_EVENT_SCOPE(UFSEDataTask_Load::DeserializeActorComponents);

if (Filter.StoresAnyComponents())
if (ComponentFilter.IsAnyAllowed())
{
for (auto* Component : Actor->GetComponents())
{
TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeActor | Component);
if (!IsValid(Component) || !Filter.Stores(Component))
if (!IsValid(Component) || !ComponentFilter.IsAllowed(Component->GetClass()))
{
continue;
}
Expand Down Expand Up @@ -248,9 +252,43 @@ bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, cons
return true;
}

void SERecords::SerializePlayer(const APlayerState* PlayerState, FActorRecord& Record) {}
void SERecords::SerializePlayer(const APlayerState* PlayerState, FPlayerRecord& Record, const FSEClassFilter& ComponentFilter)
{
check(PlayerState);

APlayerController* PC = PlayerState->GetPlayerController();
APawn* Pawn = PlayerState->GetPawn();

Record.UniqueId = PlayerState->GetUniqueId();
SERecords::SerializeActor(PlayerState, Record.PlayerState, ComponentFilter);
if (Pawn)
{
SERecords::SerializeActor(Pawn, Record.Pawn, ComponentFilter);
}
if (PC)
{
SERecords::SerializeActor(PC, Record.Controller, ComponentFilter);
}
}

void SERecords::DeserializePlayer(APlayerState* PlayerState, const FActorRecord& Record) {}
void SERecords::DeserializePlayer(APlayerState* PlayerState, const FPlayerRecord& Record, const FSEClassFilter& ComponentFilter)
{
check(PlayerState);
check(PlayerState->GetUniqueId() == Record.UniqueId);

APlayerController* PC = PlayerState->GetPlayerController();
APawn* Pawn = PlayerState->GetPawn();

SERecords::DeserializeActor(PlayerState, Record.PlayerState, ComponentFilter);
if (Pawn)
{
SERecords::DeserializeActor(Pawn, Record.Pawn, ComponentFilter);
}
if (PC)
{
SERecords::DeserializeActor(PC, Record.Controller, ComponentFilter);
}
}


bool SERecords::IsSaveTag(const FName& Tag)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ void FSEDataTask_Load::DeserializeLevelSync(const ULevel* Level, const ULevelStr
const FActorRecord* Record = RecordToActor.Key;
AActor* Actor = RecordToActor.Value.Get();
check(Record && Actor);
SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter);
SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter.ComponentFilter);
}
}

Expand Down Expand Up @@ -355,7 +355,7 @@ void FSEDataTask_Load::DeserializeASyncLoop(float StartMS)
{
continue;
}
SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter);
SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter.ComponentFilter);

const float CurrentMS = GetTimeMilliseconds();
if (CurrentMS - StartMS >= MaxFrameMs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void FSEDataTask_LoadLevel::DeserializeASyncLoop(float StartMS /*= 0.0f*/)
{
continue;
}
SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter);
SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter.ComponentFilter);

const float CurrentMS = GetTimeMilliseconds();
if (CurrentMS - StartMS >= MaxFrameMs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

FSEDataTask_Save::FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot)
: FSEDataTask(Manager, ESETaskType::Save)
, SlotData(Slot->GetData())
, SlotData(Slot->AssureData())
{}

FSEDataTask_Save::~FSEDataTask_Save()
Expand Down Expand Up @@ -68,9 +68,6 @@ void FSEDataTask_Save::OnStart()

Manager->OnSaveBegan();

Slot = Manager->GetActiveSlot();
SlotData = Slot->GetData();

check(SlotData->GetClass() == Slot->DataClass);
SlotData->CleanRecords(true);

Expand Down Expand Up @@ -271,7 +268,7 @@ void FSEDataTask_Save::SerializeLevel(

ParallelFor(ActorsToSerialize.Num(), [&LevelRecord, &ActorsToSerialize, &Filter](int32 i)
{
SERecords::SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter);
SERecords::SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter.ComponentFilter);
}, Slot->ShouldSerializeAsync()? EParallelForFlags::None : EParallelForFlags::ForceSingleThread);
}

Expand Down
1 change: 0 additions & 1 deletion Source/SaveExtension/Public/LevelFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,5 @@ struct FSELevelFilter
void BakeAllowedClasses() const;

bool Stores(const AActor* Actor) const;
bool StoresAnyComponents() const;
bool Stores(const UActorComponent* Component) const;
};
4 changes: 4 additions & 0 deletions Source/SaveExtension/Public/SaveSlot.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame
Data = NewData;
}

// Gets the data instance, and creates it if wasn't already
UFUNCTION(BlueprintCallable, Category = SaveSlot)
USaveSlotData* AssureData();

bool ShouldDeserializeAsync() const;
bool ShouldSerializeAsync() const;

Expand Down
15 changes: 14 additions & 1 deletion Source/SaveExtension/Public/SaveSlotData.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#include "SaveSlotData.generated.h"


struct FUniqueNetIdRepl;


/**
* USaveSlotData stores all information that can be accessible only while the game is loaded.
* Works like a common SaveGame object
Expand All @@ -27,7 +30,7 @@ class SAVEEXTENSION_API USaveSlotData : public UObject

public:
/** Game world time since game started in seconds */
UPROPERTY(SaveGame, Category = SaveData, BlueprintReadOnly)
UPROPERTY(SaveGame, Category = SaveSlotData, BlueprintReadOnly)
float TimeSeconds;

/** Records
Expand All @@ -42,9 +45,19 @@ class SAVEEXTENSION_API USaveSlotData : public UObject
FPersistentLevelRecord RootLevel;
TArray<FStreamingLevelRecord> SubLevels;

TArray<FPlayerRecord> Players;


void CleanRecords(bool bKeepSublevels);

/** Using manual serialization. It's way faster than reflection serialization */
virtual void Serialize(FArchive& Ar) override;

UFUNCTION(BlueprintPure, Category = SaveSlotData)
FPlayerRecord& FindOrAddPlayerRecord(const FUniqueNetIdRepl& UniqueId);
FPlayerRecord* FindPlayerRecord(const FUniqueNetIdRepl& UniqueId);
UFUNCTION(BlueprintPure, Category = SaveSlotData)
bool FindPlayerRecord(const FUniqueNetIdRepl& UniqueId, UPARAM(Ref) FPlayerRecord& Record);
UFUNCTION(BlueprintPure, Category = SaveSlotData)
bool RemovePlayerRecord(const FUniqueNetIdRepl& UniqueId);
};
28 changes: 19 additions & 9 deletions Source/SaveExtension/Public/Serialization/Records.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
#pragma once

#include <CoreMinimal.h>
#include <Engine/LevelScriptActor.h>
#include <Engine/LevelStreaming.h>

#include "Records.generated.h"


struct FSELevelFilter;
struct FSEClassFilter;
class USaveSlotData;
class APlayerState;


USTRUCT()
Expand Down Expand Up @@ -123,11 +122,22 @@ struct FSubsystemRecord : public FObjectRecord
FSubsystemRecord(const USubsystem* Subsystem) : Super(Subsystem) {}
};

USTRUCT()
struct FControlledRecord
USTRUCT(BlueprintType)
struct FPlayerRecord
{
GENERATED_BODY()

FUniqueNetIdRepl UniqueId;

FActorRecord PlayerState;
FActorRecord Controller;
FActorRecord Pawn;


bool operator==(const FPlayerRecord& Other) const
{
return UniqueId == Other.UniqueId;
}
};


Expand All @@ -138,10 +148,10 @@ namespace SERecords
extern const FName TagNoTags;


void SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFilter& Filter);
bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter);
void SerializePlayer(const APlayerState* PlayerState, FActorRecord& Record);
void DeserializePlayer(APlayerState* PlayerState, const FActorRecord& Record);
void SerializeActor(const AActor* Actor, FActorRecord& Record, const FSEClassFilter& ComponentFilter);
bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSEClassFilter& ComponentFilter);
void SerializePlayer(const APlayerState* PlayerState, FPlayerRecord& Record, const FSEClassFilter& ComponentFilter);
void DeserializePlayer(APlayerState* PlayerState, const FPlayerRecord& Record, const FSEClassFilter& ComponentFilter);

bool IsSaveTag(const FName& Tag);
bool StoresTransform(const AActor* Actor);
Expand Down