171 lines
6.7 KiB
C++
171 lines
6.7 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(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();
|
|
|
|
static EAgrarianWeatherType MapOpenMeteoWeatherCode(int32 WeatherCode, float PrecipitationMm, float WindSpeedKmh);
|
|
|
|
private:
|
|
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;
|
|
};
|