diff --git a/Source/AutoSave/Private/AutoSaveSubsystem.cpp b/Source/AutoSave/Private/AutoSaveSubsystem.cpp index 22f7508..3cff9c8 100644 --- a/Source/AutoSave/Private/AutoSaveSubsystem.cpp +++ b/Source/AutoSave/Private/AutoSaveSubsystem.cpp @@ -30,22 +30,30 @@ void UAutoSaveSubsystem::GetSaveStructInfosWithoutData(TArray& FSaveStruct * UAutoSaveSubsystem::AddSaveStructRef(const FString& Filename, UScriptStruct * ScriptStruct) { - const bool bIsCppStruct = ScriptStruct->IsChildOf(FSaveStruct::StaticStruct()); - const bool bIsBlueprintStruct = ScriptStruct->GetClass() == UUserDefinedStruct::StaticClass(); - - if (!bIsCppStruct && !bIsBlueprintStruct) - return nullptr; - if (StructInfos.Contains(Filename)) { FSaveStructInfo* StructInfo = StructInfos[Filename].Get(); + if (ScriptStruct && ScriptStruct != StructInfo->Struct) + { + UE_LOG(LogAutoSave, Warning, TEXT("The requested Save Struct '%s' type conflicts with the existing."), *Filename); + return nullptr; + } + // Increase the reference count of SaveStruct by one, and then decrease it accordingly in UAutoSaveSubsystem::RemoveSaveStructRef StructInfo->RefConut++; return (FSaveStruct*)StructInfo->Data.GetData(); } + if (!ScriptStruct) return nullptr; + + const bool bIsCppStruct = ScriptStruct->IsChildOf(FSaveStruct::StaticStruct()); + const bool bIsBlueprintStruct = ScriptStruct->GetClass() == UUserDefinedStruct::StaticClass(); + + if (!bIsCppStruct && !bIsBlueprintStruct) + return nullptr; + TUniquePtr NewStructInfo(new FSaveStructInfo()); if (FPaths::FileExists(Filename)) @@ -53,6 +61,7 @@ FSaveStruct * UAutoSaveSubsystem::AddSaveStructRef(const FString& Filename, UScr NewStructInfo->Filename = Filename; NewStructInfo->Struct = ScriptStruct; NewStructInfo->Data.SetNumUninitialized(ScriptStruct->GetStructureSize()); + ScriptStruct->InitializeStruct(NewStructInfo->Data.GetData()); NewStructInfo->State = ESaveStructState::Preload; NewStructInfo->RefConut = 1; NewStructInfo->LastRefConut = 0; @@ -112,7 +121,7 @@ UAutoSaveSubsystem::FStructLoadOrSaveTask::FStructLoadOrSaveTask(FSaveStructInfo { case ESaveStructState::Preload: StructInfoPtr->State = ESaveStructState::Loading; - DataCopy.SetNumUninitialized(StructInfoPtr->Data.Num()); + DataCopy = StructInfoPtr->Data; break; case ESaveStructState::Idle: @@ -292,12 +301,30 @@ void UAutoSaveSubsystem::Initialize(FSubsystemCollectionBase & Collection) void UAutoSaveSubsystem::Deinitialize() { + // Make sure the tasks are completed for (TUniquePtr>& Task : TaskThreads) { if (!Task) continue; Task->EnsureCompletion(); Task = nullptr; } + + // Make sure objects are saved + for (const TPair>& Info : StructInfos) + { + // Skip objects that are not loaded + if (Info.Value->State == ESaveStructState::Preload) continue; + + check(Info.Value->State == ESaveStructState::Idle); + + if (Info.Value->RefConut > 0) + { + UE_LOG(LogAutoSave, Warning, TEXT("The subsystem deinitialize, but '%s' still has references."), *Info.Value->Filename); + } + + FAsyncTask Task(Info.Value.Get()); + Task.StartSynchronousTask(); + } } void UAutoSaveSubsystem::Tick(float DeltaTime) diff --git a/Source/AutoSave/Private/Blueprint/AutoSaveBlueprintLibrary.cpp b/Source/AutoSave/Private/Blueprint/AutoSaveBlueprintLibrary.cpp index 51b61ec..11e43f1 100644 --- a/Source/AutoSave/Private/Blueprint/AutoSaveBlueprintLibrary.cpp +++ b/Source/AutoSave/Private/Blueprint/AutoSaveBlueprintLibrary.cpp @@ -1,6 +1,5 @@ #include "Blueprint/AutoSaveBlueprintLibrary.h" -#include "AutoSaveSubsystem.h" #include "Kismet/GameplayStatics.h" bool UAutoSaveBlueprintLibrary::AddSaveStructRef(UObject * WorldContextObject, const FString & Filename, UScriptStruct * ScriptStruct) @@ -28,3 +27,49 @@ void UAutoSaveBlueprintLibrary::RemoveSaveStructRef(UObject * WorldContextObject AutoSaveSubsystem->RemoveSaveStructRef(Filename); } + +bool UAutoSaveBlueprintLibrary::Generic_TryGetSaveStruct(UObject * WorldContextObject, const FString & Filename, UScriptStruct * ScriptStruct, void * Value) +{ + UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(WorldContextObject); + + if (!GameInstance) return false; + + UAutoSaveSubsystem* AutoSaveSubsystem = GameInstance->GetSubsystem(); + + if (!AutoSaveSubsystem) return false; + + if (!AutoSaveSubsystem->StructInfos.Contains(Filename)) return false; + + FSaveStructInfo* Info = AutoSaveSubsystem->StructInfos[Filename].Get(); + + if (Info->State == ESaveStructState::Preload || Info->State == ESaveStructState::Loading) return false; + + if (Info->Struct != ScriptStruct) return false; + + ScriptStruct->CopyScriptStruct(Value, Info->Data.GetData()); + + return true; +} + +bool UAutoSaveBlueprintLibrary::Generic_TrySetSaveStruct(UObject * WorldContextObject, const FString & Filename, UScriptStruct * ScriptStruct, void * Value) +{ + UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(WorldContextObject); + + if (!GameInstance) return false; + + UAutoSaveSubsystem* AutoSaveSubsystem = GameInstance->GetSubsystem(); + + if (!AutoSaveSubsystem) return false; + + if (!AutoSaveSubsystem->StructInfos.Contains(Filename)) return false; + + FSaveStructInfo* Info = AutoSaveSubsystem->StructInfos[Filename].Get(); + + if (Info->State == ESaveStructState::Preload || Info->State == ESaveStructState::Loading) return false; + + if (Info->Struct != ScriptStruct) return false; + + ScriptStruct->CopyScriptStruct(Info->Data.GetData(), Value); + + return true; +} diff --git a/Source/AutoSave/Private/SaveStruct.cpp b/Source/AutoSave/Private/SaveStruct.cpp index ab13c45..e69de29 100644 --- a/Source/AutoSave/Private/SaveStruct.cpp +++ b/Source/AutoSave/Private/SaveStruct.cpp @@ -1 +0,0 @@ -#include "SaveStruct.h" diff --git a/Source/AutoSave/Public/AutoSaveSubsystem.h b/Source/AutoSave/Public/AutoSaveSubsystem.h index 159ba92..df14414 100644 --- a/Source/AutoSave/Public/AutoSaveSubsystem.h +++ b/Source/AutoSave/Public/AutoSaveSubsystem.h @@ -17,7 +17,7 @@ enum class ESaveStructState : uint8 }; USTRUCT(BlueprintType) -struct FSaveStructInfo +struct AUTOSAVE_API FSaveStructInfo { GENERATED_BODY() @@ -50,7 +50,7 @@ class AUTOSAVE_API UAutoSaveSubsystem : public UGameInstanceSubsystem, public FT { GENERATED_BODY() - friend struct FSaveStructPtr; + friend class UAutoSaveBlueprintLibrary; public: @@ -65,7 +65,7 @@ public: UFUNCTION(BlueprintCallable, Category = "AutoSave") void GetSaveStructInfosWithoutData(TArray& OutSaveStructInfos) const; - FSaveStruct* AddSaveStructRef(const FString& Filename, UScriptStruct* ScriptStruct); + FSaveStruct* AddSaveStructRef(const FString& Filename, UScriptStruct* ScriptStruct = nullptr); void RemoveSaveStructRef(const FString& Filename); diff --git a/Source/AutoSave/Public/Blueprint/AutoSaveBlueprintLibrary.h b/Source/AutoSave/Public/Blueprint/AutoSaveBlueprintLibrary.h index e66aec3..db45c62 100644 --- a/Source/AutoSave/Public/Blueprint/AutoSaveBlueprintLibrary.h +++ b/Source/AutoSave/Public/Blueprint/AutoSaveBlueprintLibrary.h @@ -1,6 +1,8 @@ #pragma once #include "CoreMinimal.h" +#include "SaveStruct.h" +#include "AutoSaveSubsystem.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "AutoSaveBlueprintLibrary.generated.h" @@ -16,5 +18,46 @@ public: UFUNCTION(BlueprintCallable, Category = "AutoSave", meta = (WorldContext = "WorldContextObject")) static void RemoveSaveStructRef(UObject* WorldContextObject, const FString& Filename); + + UFUNCTION(BlueprintCallable, Category = "AutoSave", meta = (WorldContext = "WorldContextObject", CustomStructureParam = "Value"), CustomThunk) + static void TryGetSaveStruct(UObject* WorldContextObject, const FString& Filename, int32& Value, bool& bSuccess) { checkNoEntry(); } + static bool Generic_TryGetSaveStruct(UObject* WorldContextObject, const FString& Filename, UScriptStruct* ScriptStruct, void* Value); + DECLARE_FUNCTION(execTryGetSaveStruct) + { + P_GET_OBJECT(UObject, WorldContextObject); + P_GET_PROPERTY_REF(FStrProperty, Filename); + Stack.Step(Stack.Object, nullptr); + void* StructPtr = Stack.MostRecentPropertyAddress; + FStructProperty* StructProperty = CastField(Stack.MostRecentProperty); + + P_GET_UBOOL_REF(bSuccess); + + P_FINISH; + + P_NATIVE_BEGIN; + bSuccess = Generic_TryGetSaveStruct(WorldContextObject, Filename, StructProperty ? StructProperty->Struct : nullptr, StructPtr); + P_NATIVE_END; + } + + UFUNCTION(BlueprintCallable, Category = "AutoSave", meta = (WorldContext = "WorldContextObject", CustomStructureParam = "Value"), CustomThunk) + static void TrySetSaveStruct(UObject* WorldContextObject, const FString& Filename, const int32& Value, bool& bSuccess) { checkNoEntry(); } + static bool Generic_TrySetSaveStruct(UObject* WorldContextObject, const FString& Filename, UScriptStruct* ScriptStruct, void* Value); + DECLARE_FUNCTION(execTrySetSaveStruct) + { + P_GET_OBJECT(UObject, WorldContextObject); + P_GET_PROPERTY_REF(FStrProperty, Filename); + + Stack.Step(Stack.Object, nullptr); + void* StructPtr = Stack.MostRecentPropertyAddress; + FStructProperty* StructProperty = CastField(Stack.MostRecentProperty); + + P_GET_UBOOL_REF(bSuccess); + + P_FINISH; + + P_NATIVE_BEGIN; + bSuccess = Generic_TrySetSaveStruct(WorldContextObject, Filename, StructProperty ? StructProperty->Struct : nullptr, StructPtr); + P_NATIVE_END; + } }; diff --git a/Source/AutoSave/Public/SaveStruct.h b/Source/AutoSave/Public/SaveStruct.h index 80f2937..e69de29 100644 --- a/Source/AutoSave/Public/SaveStruct.h +++ b/Source/AutoSave/Public/SaveStruct.h @@ -1,10 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "SaveStruct.generated.h" - -USTRUCT(BlueprintType) -struct AUTOSAVE_API FSaveStructPtr -{ - GENERATED_BODY() -};