This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
AgrarianGameArchive/Source/AgrarianGame/AgrarianWeatherProviderSubsystem.h
T

181 lines
7.3 KiB
C++

// Copyright Pacificao. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "HttpFwd.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "AgrarianTypes.h"
#include "AgrarianWeatherProviderSubsystem.generated.h"
class AAgrarianGameState;
class IHttpRequest;
class IHttpResponse;
USTRUCT(BlueprintType)
struct FAgrarianWeatherProviderSnapshot
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
FName TileId = NAME_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float Latitude = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float Longitude = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
FString Provider = TEXT("open-meteo");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
FString ProviderTimestamp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float CurrentTemperatureC = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float DailyLowTemperatureC = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float DailyHighTemperatureC = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float PrecipitationMm = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float WindSpeedKmh = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float CloudCoverPercent = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float RelativeHumidityPercent = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float PressureMslHpa = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float VisibilityMeters = 10000.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
int32 WeatherCode = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
EAgrarianWeatherType MappedWeather = EAgrarianWeatherType::Clear;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
bool bIsValid = false;
};
USTRUCT(BlueprintType)
struct FAgrarianCachedWeatherSnapshot
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
FAgrarianWeatherProviderSnapshot Snapshot;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
FDateTime CachedAtUtc = FDateTime::MinValue();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
float TimeToLiveSeconds = 900.0f;
bool IsFresh(const FDateTime& NowUtc) const
{
return Snapshot.bIsValid
&& CachedAtUtc != FDateTime::MinValue()
&& (NowUtc - CachedAtUtc).GetTotalSeconds() <= TimeToLiveSeconds;
}
};
UCLASS()
class UAgrarianWeatherProviderSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
bool bEnableLiveWeatherRequests = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
FString OpenMeteoForecastEndpoint = TEXT("https://api.open-meteo.com/v1/forecast");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
bool bEnableNoaaNwsFallbackForUSTiles = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
FString NoaaNwsPointsEndpoint = TEXT("https://api.weather.gov/points");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
FString NoaaNwsUserAgent = TEXT("AgrarianGameMVP/0.1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather", meta = (ClampMin = "60.0"))
float WeatherSnapshotCacheTtlSeconds = 900.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
bool bEnableDeterministicFallbackWeather = true;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather")
FAgrarianWeatherProviderSnapshot LastSnapshot;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather")
FString LastNoaaNwsForecastGridDataUrl;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather")
TMap<FString, FAgrarianCachedWeatherSnapshot> ServerWeatherSnapshotCache;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
bool RequestWeatherForActiveGameState();
UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
bool RequestWeatherForTile(FName TileId, float Latitude, float Longitude);
UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
FString BuildOpenMeteoForecastUrl(FName TileId, float Latitude, float Longitude) const;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
bool RequestNoaaNwsFallbackForTile(FName TileId, float Latitude, float Longitude);
UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
bool IsNoaaNwsEligibleCoordinate(float Latitude, float Longitude) const;
UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
FString BuildNoaaNwsPointsUrl(float Latitude, float Longitude) const;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
bool ApplySnapshotToGameState(const FAgrarianWeatherProviderSnapshot& Snapshot, AAgrarianGameState* GameState) const;
UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
FAgrarianMappedWeatherInputs MapSnapshotToAgrarianWeatherInputs(const FAgrarianWeatherProviderSnapshot& Snapshot) const;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
bool TryApplyCachedSnapshot(FName TileId, const FString& Provider, AAgrarianGameState* GameState);
UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
bool HasFreshCachedSnapshot(FName TileId, const FString& Provider) const;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
void ClearWeatherSnapshotCache();
UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
bool ApplyDeterministicFallbackWeather(FName TileId, float Latitude, float Longitude, AAgrarianGameState* GameState);
UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
FAgrarianWeatherProviderSnapshot BuildDeterministicFallbackSnapshot(FName TileId, float Latitude, float Longitude, int32 DayOfYear, float HourOfDay) const;
static EAgrarianWeatherType MapOpenMeteoWeatherCode(int32 WeatherCode, float PrecipitationMm, float WindSpeedKmh);
private:
float GetDeterministicWeatherNoise(FName TileId, int32 DayOfYear, int32 Salt) const;
FString MakeCacheKey(FName TileId, const FString& Provider) const;
void CacheSnapshot(const FAgrarianWeatherProviderSnapshot& Snapshot, float TimeToLiveSeconds);
void OnOpenMeteoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FName TileId, float Latitude, float Longitude);
void OnNoaaNwsPointsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FName TileId, float Latitude, float Longitude);
void OnNoaaNwsGridDataResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FName TileId, float Latitude, float Longitude);
bool ParseOpenMeteoForecast(const FString& ResponseContent, FName TileId, float Latitude, float Longitude, FAgrarianWeatherProviderSnapshot& OutSnapshot) const;
bool ParseNoaaNwsGridData(const FString& ResponseContent, FName TileId, float Latitude, float Longitude, FAgrarianWeatherProviderSnapshot& OutSnapshot) const;
};