Add weather save load support

This commit is contained in:
2026-05-16 00:56:11 -07:00
parent 26ddf8ea8e
commit 8625583faa
7 changed files with 163 additions and 7 deletions
@@ -5,6 +5,7 @@
#include "AgrarianBuildingPlacementComponent.h"
#include "AgrarianCraftingComponent.h"
#include "AgrarianGameCharacter.h"
#include "AgrarianGameState.h"
#include "AgrarianInteractable.h"
#include "AgrarianInventoryComponent.h"
#include "AgrarianPersistentActorComponent.h"
@@ -628,6 +629,36 @@ FString UAgrarianEditorAutomationLibrary::RunPersistenceSubsystemSmokeTest(TSubc
Persistence->RegisterWorldActorClass(TEXT("primitive_shelter"), ShelterClass);
UGameplayStatics::DeleteGameInSlot(EffectiveSlotName, 0);
AAgrarianGameState* GameState = TestWorld->GetGameState<AAgrarianGameState>();
if (!GameState)
{
Persistence->DefaultSlotName = PreviousSlotName;
Persistence->UserIndex = PreviousUserIndex;
Persistence->WorldActorClassRegistry = PreviousRegistry;
return TEXT("FAIL: no Agrarian game state found for persistence test");
}
FAgrarianMappedWeatherInputs SavedWeatherInputs;
SavedWeatherInputs.TileId = TEXT("automation_weather_tile");
SavedWeatherInputs.Latitude = 37.5925f;
SavedWeatherInputs.Longitude = -122.4995f;
SavedWeatherInputs.TemperatureC = 9.5f;
SavedWeatherInputs.DailyLowTemperatureC = 7.0f;
SavedWeatherInputs.DailyHighTemperatureC = 13.0f;
SavedWeatherInputs.PrecipitationMm = 4.0f;
SavedWeatherInputs.WindSpeedKmh = 18.0f;
SavedWeatherInputs.CloudCoverPercent = 85.0f;
SavedWeatherInputs.RelativeHumidityPercent = 92.0f;
SavedWeatherInputs.PressureMslHpa = 1007.0f;
SavedWeatherInputs.VisibilityMeters = 6000.0f;
SavedWeatherInputs.ProviderWeatherCode = 61;
SavedWeatherInputs.MappedWeather = EAgrarianWeatherType::Rain;
SavedWeatherInputs.Provider = TEXT("automation-weather");
SavedWeatherInputs.ProviderTimestamp = TEXT("2026-05-16T08:00:00Z");
SavedWeatherInputs.bHasProviderData = true;
GameState->WorldHours = 14.25f;
GameState->ApplyMappedWeatherInputs(SavedWeatherInputs);
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AActor* TestShelter = TestWorld->SpawnActor<AActor>(
@@ -686,7 +717,20 @@ FString UAgrarianEditorAutomationLibrary::RunPersistenceSubsystemSmokeTest(TSubc
return TEXT("FAIL: loaded save did not include persistent world actors");
}
const int32 RestoredActorCount = Persistence->RestoreWorldActors(LoadedSave, true);
GameState->WorldHours = 3.5f;
GameState->SetWeather(EAgrarianWeatherType::Storm);
int32 RestoredPlayerCount = 0;
int32 RestoredActorCount = 0;
if (!Persistence->LoadCurrentWorld(RestoredPlayerCount, RestoredActorCount, true))
{
UGameplayStatics::DeleteGameInSlot(EffectiveSlotName, 0);
Persistence->DefaultSlotName = PreviousSlotName;
Persistence->UserIndex = PreviousUserIndex;
Persistence->WorldActorClassRegistry = PreviousRegistry;
return TEXT("FAIL: LoadCurrentWorld failed to restore world state");
}
if (RestoredActorCount != SavedActorCount)
{
UGameplayStatics::DeleteGameInSlot(EffectiveSlotName, 0);
@@ -696,6 +740,23 @@ FString UAgrarianEditorAutomationLibrary::RunPersistenceSubsystemSmokeTest(TSubc
return FString::Printf(TEXT("FAIL: restored actor count mismatch, saved=%d restored=%d"), SavedActorCount, RestoredActorCount);
}
const FAgrarianWeatherDebugSnapshot RestoredWeather = GameState->GetWeatherDebugSnapshot();
const bool bWeatherRestored =
FMath::IsNearlyEqual(GameState->WorldHours, 14.25f, 0.01f) &&
GameState->Weather == EAgrarianWeatherType::Rain &&
RestoredWeather.TileId == SavedWeatherInputs.TileId &&
RestoredWeather.Provider == SavedWeatherInputs.Provider &&
RestoredWeather.ProviderTimestamp == SavedWeatherInputs.ProviderTimestamp &&
RestoredWeather.AppliedWeather == EAgrarianWeatherType::Rain;
if (!bWeatherRestored)
{
UGameplayStatics::DeleteGameInSlot(EffectiveSlotName, 0);
Persistence->DefaultSlotName = PreviousSlotName;
Persistence->UserIndex = PreviousUserIndex;
Persistence->WorldActorClassRegistry = PreviousRegistry;
return TEXT("FAIL: weather state did not survive save/load");
}
int32 PersistentActorCountAfterRestore = 0;
for (TActorIterator<AActor> ActorIt(TestWorld); ActorIt; ++ActorIt)
{
@@ -713,9 +774,12 @@ FString UAgrarianEditorAutomationLibrary::RunPersistenceSubsystemSmokeTest(TSubc
Persistence->WorldActorClassRegistry = PreviousRegistry;
return FString::Printf(
TEXT("PASS: live persistence subsystem saved %d actor(s), restored %d actor(s), world now has %d persistent actor(s)"),
TEXT("PASS: live persistence subsystem saved %d actor(s), restored %d actor(s), restored %d player(s), restored weather %s from %s, world now has %d persistent actor(s)"),
SavedActorCount,
RestoredActorCount,
RestoredPlayerCount,
*UEnum::GetValueAsString(GameState->Weather),
*RestoredWeather.Provider,
PersistentActorCountAfterRestore);
#else
return TEXT("FAIL: editor automation is only available in editor builds");
@@ -168,10 +168,14 @@ void AAgrarianGamePlayerController::ServerAgrarianLoadWorld_Implementation()
}
Persistence->RegisterWorldActorClass(TEXT("primitive_shelter"), AAgrarianShelterActor::StaticClass());
const UAgrarianSaveGame* SaveGame = Persistence->LoadOrCreateSave();
const int32 RestoredPlayerCount = Persistence->RestorePlayers(SaveGame);
const int32 RestoredCount = Persistence->RestoreWorldActors(SaveGame);
ClientMessage(FString::Printf(TEXT("Agrarian world loaded. Restored players: %d. Restored actors: %d."), RestoredPlayerCount, RestoredCount));
int32 RestoredPlayerCount = 0;
int32 RestoredActorCount = 0;
const bool bLoaded = Persistence->LoadCurrentWorld(RestoredPlayerCount, RestoredActorCount);
ClientMessage(FString::Printf(
TEXT("%s Restored players: %d. Restored actors: %d."),
bLoaded ? TEXT("Agrarian world loaded.") : TEXT("Agrarian world load restored actors/players, but world state restore failed."),
RestoredPlayerCount,
RestoredActorCount));
}
void AAgrarianGamePlayerController::ServerAgrarianHeal_Implementation()
@@ -254,6 +254,23 @@ bool UAgrarianPersistenceSubsystem::SaveCurrentWorld() const
return WriteSave(SaveGame);
}
bool UAgrarianPersistenceSubsystem::LoadCurrentWorld(int32& RestoredPlayerCount, int32& RestoredWorldActorCount, bool bClearExistingActors) const
{
RestoredPlayerCount = 0;
RestoredWorldActorCount = 0;
const UAgrarianSaveGame* SaveGame = LoadOrCreateSave();
if (!SaveGame)
{
return false;
}
const bool bRestoredWorldState = RestoreWorldState(SaveGame);
RestoredPlayerCount = RestorePlayers(SaveGame);
RestoredWorldActorCount = RestoreWorldActors(SaveGame, bClearExistingActors);
return bRestoredWorldState;
}
void UAgrarianPersistenceSubsystem::FindPersistentComponents(TArray<UAgrarianPersistentActorComponent*>& OutComponents) const
{
OutComponents.Reset();
@@ -61,6 +61,9 @@ public:
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
bool SaveCurrentWorld() const;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
bool LoadCurrentWorld(int32& RestoredPlayerCount, int32& RestoredWorldActorCount, bool bClearExistingActors = true) const;
protected:
void FindPersistentComponents(TArray<UAgrarianPersistentActorComponent*>& OutComponents) const;
void FindAgrarianPlayers(TArray<AAgrarianGameCharacter*>& OutPlayers) const;