// Copyright IHY. Vertical Partition Streaming plugin. // // The runtime streaming engine. World-scoped, holds the per-cell state machine, // the async load queue, the proxy actors and the live statistics. Driven each // update tick by AVerticalPartitionManager (subsystems are not ticked directly). #pragma once #include "CoreMinimal.h" #include "Subsystems/WorldSubsystem.h" #include "VerticalPartitionTypes.h" #include "VerticalPartitionStatics.h" #include "VerticalPartitionRuntimeSubsystem.generated.h" class UVerticalPartitionDescriptor; class ULevelStreaming; class AStaticMeshActor; /** Per-cell live runtime record. Plain struct (C++-only, same-module access). */ struct FVerticalCellRuntime { FVerticalCellId Id; int32 DescriptorIndex = INDEX_NONE; FBox Bounds = FBox(ForceInit); EVerticalCellState State = EVerticalCellState::Unloaded; EVerticalCellDesiredState Desired = EVerticalCellDesiredState::Unloaded; /** Backend B (DestructiveCommit): the streaming sub-level for the full actors. */ TWeakObjectPtr StreamingLevel; /** Backend A (NonDestructive): the original persistent-level actors of this cell. */ TArray> ResolvedActors; /** Shown while the cell is HLOD. */ TWeakObjectPtr ProxyActor; bool bUsesStreamingBackend = false; // true => backend B bool bPreloadRequested = false; bool bProxyMissingWarned = false; /** Debug lock: when set, Desired is pinned to ForcedState. */ bool bForced = false; EVerticalCellDesiredState ForcedState = EVerticalCellDesiredState::Full; float EstimatedMemoryMB = 0.f; double LastChangeTime = 0.0; }; UCLASS() class VERTICALPARTITION_API UVerticalPartitionRuntimeSubsystem : public UWorldSubsystem { GENERATED_BODY() public: static UVerticalPartitionRuntimeSubsystem* Get(const UObject* WorldContextObject); virtual void Deinitialize() override; // ---- lifecycle (called by the manager) ---- void RegisterDescriptor(UVerticalPartitionDescriptor* InDescriptor, const FBox& InVolumeBounds); void UnregisterDescriptor(); bool IsReady() const { return Descriptor != nullptr; } /** Main entry point. Recomputes desired states and steps the machine within the * per-update async budget. bSnap forces immediate convergence (teleport/checkpoint). */ void UpdateStreaming(const FVerticalStreamingContext& Ctx, float DeltaSeconds, bool bSnap = false); // ---- live range overrides (console) ---- FVerticalPartitionSettings& GetMutableSettings() { return EffectiveSettings; } const FVerticalPartitionSettings& GetSettings() const { return EffectiveSettings; } void SetFullRange(int32 Below, int32 Above); void SetHLODRange(int32 Below, int32 Above); void SetCellHeight(float NewHeight); // ---- debug forcing ---- void ForceCellFull(int32 Z); void ForceCellHLOD(int32 Z); void UnforceCell(int32 Z); void UnforceAll(); void ShowOnlyCell(int32 Z); // force one Full, everything else Unloaded // ---- introspection ---- const FVerticalRuntimeStats& GetStats() const { return Stats; } const TMap& GetCellRuntimes() const { return Cells; } const FBox& GetVolumeBounds() const { return VolumeBounds; } UVerticalPartitionDescriptor* GetDescriptor() const { return Descriptor; } int32 GetPlayerCellZ() const { return Stats.PlayerCellZ; } // ---- reporting (console) ---- void DumpRuntimeStats() const; void DumpCells() const; void DumpWarnings() const; private: UPROPERTY(Transient) TObjectPtr Descriptor = nullptr; FVerticalPartitionSettings EffectiveSettings; FBox VolumeBounds = FBox(ForceInit); TMap Cells; FVerticalRuntimeStats Stats; bool bShowOnlyMode = false; int32 ShowOnlyZ = 0; // ---- state-machine helpers (the spec's key runtime functions) ---- void ApplyDesiredState(FVerticalCellRuntime& Cell, bool bSnap, int32& LoadBudget, int32& UnloadBudget); void LoadCellFullAsync(FVerticalCellRuntime& Cell, bool bVisible); void ShowCellHLOD(FVerticalCellRuntime& Cell); void UnloadCell(FVerticalCellRuntime& Cell); void PollStreaming(FVerticalCellRuntime& Cell); void SetActorGroupActive(FVerticalCellRuntime& Cell, bool bVisible, bool bCollision, bool bTick); AStaticMeshActor* EnsureProxy(FVerticalCellRuntime& Cell); void HideProxy(FVerticalCellRuntime& Cell); ULevelStreaming* EnsureStreamingLevel(FVerticalCellRuntime& Cell); void RebuildStatsFromCells(); FVerticalCellRuntime* FindCellByZ(int32 Z); // first cell at this Z (XY=0 path) };