From 4a9c51861d79192194f1aa573378a4dab578b7f5 Mon Sep 17 00:00:00 2001 From: muit Date: Mon, 9 Oct 2023 13:21:02 +0200 Subject: [PATCH] Added player records and serialization --- Source/SaveExtension/Private/LevelFilter.cpp | 4 -- Source/SaveExtension/Private/SaveSlot.cpp | 9 +++ Source/SaveExtension/Private/SaveSlotData.cpp | 34 +++++++++++ .../Private/Serialization/Records.cpp | 56 ++++++++++++++++--- .../Private/Serialization/SEDataTask_Load.cpp | 4 +- .../Serialization/SEDataTask_LoadLevel.cpp | 2 +- .../Private/Serialization/SEDataTask_Save.cpp | 7 +-- Source/SaveExtension/Public/LevelFilter.h | 1 - Source/SaveExtension/Public/SaveSlot.h | 4 ++ Source/SaveExtension/Public/SaveSlotData.h | 15 ++++- .../Public/Serialization/Records.h | 28 +++++++--- 11 files changed, 132 insertions(+), 32 deletions(-) diff --git a/Source/SaveExtension/Private/LevelFilter.cpp b/Source/SaveExtension/Private/LevelFilter.cpp index eb5314a..9ec288c 100644 --- a/Source/SaveExtension/Private/LevelFilter.cpp +++ b/Source/SaveExtension/Private/LevelFilter.cpp @@ -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()); diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 2cc6be5..1d0cfce 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -81,6 +81,15 @@ void USaveSlot::CaptureThumbnail( } } +USaveSlotData* USaveSlot::AssureData() +{ + if (!IsValid(Data)) + { + AssignData(NewObject(this, DataClass, NAME_None)); + } + return Data; +} + bool USaveSlot::ShouldDeserializeAsync() const { return MultithreadedSerialization == ESEAsyncMode::LoadAsync || diff --git a/Source/SaveExtension/Private/SaveSlotData.cpp b/Source/SaveExtension/Private/SaveSlotData.cpp index 0a54930..d7555ab 100644 --- a/Source/SaveExtension/Private/SaveSlotData.cpp +++ b/Source/SaveExtension/Private/SaveSlotData.cpp @@ -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; +} diff --git a/Source/SaveExtension/Private/Serialization/Records.cpp b/Source/SaveExtension/Private/Serialization/Records.cpp index 1b472f5..f2d33f7 100644 --- a/Source/SaveExtension/Private/Serialization/Records.cpp +++ b/Source/SaveExtension/Private/Serialization/Records.cpp @@ -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 +#include +#include + ///////////////////////////////////////////////////// @@ -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); @@ -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(Component)) @@ -170,7 +174,7 @@ void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const const_cast(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); @@ -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; } @@ -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) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index 6723640..1b0e6fc 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -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); } } @@ -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) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp index af644af..5d0174a 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp @@ -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) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 603302a..38c5000 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -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() @@ -68,9 +68,6 @@ void FSEDataTask_Save::OnStart() Manager->OnSaveBegan(); - Slot = Manager->GetActiveSlot(); - SlotData = Slot->GetData(); - check(SlotData->GetClass() == Slot->DataClass); SlotData->CleanRecords(true); @@ -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); } diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 6e4c64e..ab33bae 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -36,6 +36,5 @@ struct FSELevelFilter void BakeAllowedClasses() const; bool Stores(const AActor* Actor) const; - bool StoresAnyComponents() const; bool Stores(const UActorComponent* Component) const; }; diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index 12aa87b..a50c03b 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -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; diff --git a/Source/SaveExtension/Public/SaveSlotData.h b/Source/SaveExtension/Public/SaveSlotData.h index 253341d..7a26919 100644 --- a/Source/SaveExtension/Public/SaveSlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -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 @@ -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 @@ -42,9 +45,19 @@ class SAVEEXTENSION_API USaveSlotData : public UObject FPersistentLevelRecord RootLevel; TArray SubLevels; + TArray 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); }; diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index 6845d9d..a04167d 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -3,14 +3,13 @@ #pragma once #include -#include -#include #include "Records.generated.h" -struct FSELevelFilter; +struct FSEClassFilter; class USaveSlotData; +class APlayerState; USTRUCT() @@ -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; + } }; @@ -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);