Dune: Awakening Item Persistence Fix

Solution for the item durability exploit in container systems

Bug Description

Players can exploit the logout/login cycle to restore damaged items to full durability after breaking containers, completely bypassing the intended item degradation mechanics.

ItemPersistenceManager.h

// ItemPersistenceManager.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Engine/World.h"
#include "TimerManager.h"
#include "ItemPersistenceManager.generated.h"

USTRUCT(BlueprintType)
struct FItemDurabilityState
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString ItemUID;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float CurrentDurability;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float MaxDurability;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FDateTime LastModified;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    bool bFromContainer;

    FItemDurabilityState()
    {
        ItemUID = "";
        CurrentDurability = 100.0f;
        MaxDurability = 100.0f;
        LastModified = FDateTime::Now();
        bFromContainer = false;
    }
};

UCLASS()
class DUNEAWAKENING_API AItemPersistenceManager : public AActor
{
    GENERATED_BODY()

public:
    AItemPersistenceManager();

protected:
    virtual void BeginPlay() override;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TMap ItemStateMap;

    FTimerHandle PersistenceTimer;

public:
    UFUNCTION(BlueprintCallable, Category = "Item Persistence")
    void TrackItemState(const FString& ItemUID, float CurrentDurability, float MaxDurability, bool bFromContainer = false);

    UFUNCTION(BlueprintCallable, Category = "Item Persistence")
    FItemDurabilityState GetItemState(const FString& ItemUID);

    UFUNCTION(BlueprintCallable, Category = "Item Persistence")
    void UpdateItemDurability(const FString& ItemUID, float NewDurability);

    UFUNCTION(BlueprintCallable, Category = "Item Persistence")
    void OnPlayerDisconnect(const FString& PlayerID);

    UFUNCTION(BlueprintCallable, Category = "Item Persistence")
    void OnPlayerConnect(const FString& PlayerID);

    UFUNCTION(BlueprintCallable, Category = "Item Persistence")
    void CleanupExpiredStates();

private:
    void PersistStateData();
    void LoadStateData();
    void ForcePersist();
};

ItemPersistenceManager.cpp

// ItemPersistenceManager.cpp
#include "ItemPersistenceManager.h"
#include "Engine/Engine.h"
#include "Kismet/GameplayStatics.h"

AItemPersistenceManager::AItemPersistenceManager()
{
    PrimaryActorTick.bCanEverTick = false;
    
    if (GetWorld())
    {
        GetWorld()->GetTimerManager().SetTimer(PersistenceTimer, this, &AItemPersistenceManager::ForcePersist, 2.0f, true);
    }
}

void AItemPersistenceManager::BeginPlay()
{
    Super::BeginPlay();
    LoadStateData();
}

void AItemPersistenceManager::TrackItemState(const FString& ItemUID, float CurrentDurability, float MaxDurability, bool bFromContainer)
{
    if (ItemUID.IsEmpty()) return;

    FItemDurabilityState NewState;
    NewState.ItemUID = ItemUID;
    NewState.CurrentDurability = CurrentDurability;
    NewState.MaxDurability = MaxDurability;
    NewState.LastModified = FDateTime::Now();
    NewState.bFromContainer = bFromContainer;

    ItemStateMap.Add(ItemUID, NewState);
    
    if (bFromContainer)
    {
        PersistStateData();
    }
}

FItemDurabilityState AItemPersistenceManager::GetItemState(const FString& ItemUID)
{
    if (ItemStateMap.Contains(ItemUID))
    {
        return ItemStateMap[ItemUID];
    }
    
    return FItemDurabilityState();
}

void AItemPersistenceManager::UpdateItemDurability(const FString& ItemUID, float NewDurability)
{
    if (ItemStateMap.Contains(ItemUID))
    {
        ItemStateMap[ItemUID].CurrentDurability = NewDurability;
        ItemStateMap[ItemUID].LastModified = FDateTime::Now();
    }
}

void AItemPersistenceManager::OnPlayerDisconnect(const FString& PlayerID)
{
    PersistStateData();
}

void AItemPersistenceManager::OnPlayerConnect(const FString& PlayerID)
{
    LoadStateData();
    CleanupExpiredStates();
}

void AItemPersistenceManager::CleanupExpiredStates()
{
    FDateTime CurrentTime = FDateTime::Now();
    TArray ExpiredItems;

    for (const auto& StatePair : ItemStateMap)
    {
        FTimespan TimeDiff = CurrentTime - StatePair.Value.LastModified;
        if (TimeDiff.GetTotalHours() > 24.0)
        {
            ExpiredItems.Add(StatePair.Key);
        }
    }

    for (const FString& ItemUID : ExpiredItems)
    {
        ItemStateMap.Remove(ItemUID);
    }
}

void AItemPersistenceManager::PersistStateData()
{
    // Hook into existing save system
    // Implementation depends on Dune's save architecture
    UE_LOG(LogTemp, Log, TEXT("Persisting %d item states"), ItemStateMap.Num());
}

void AItemPersistenceManager::LoadStateData()
{
    // Hook into existing load system
    // Implementation depends on Dune's save architecture
    UE_LOG(LogTemp, Log, TEXT("Loading item states"));
}

void AItemPersistenceManager::ForcePersist()
{
    if (ItemStateMap.Num() > 0)
    {
        PersistStateData();
    }
}

Container Integration Example

// Container integration example
void AContainer::OnContainerBroken()
{
    AItemPersistenceManager* Manager = Cast(
        UGameplayStatics::GetActorOfClass(GetWorld(), AItemPersistenceManager::StaticClass()));

    if (Manager)
    {
        for (const auto& Item : ContainerItems)
        {
            FString ItemUID = FString::Printf(TEXT("%s_%s_%lld"), 
                *Item.ItemName, 
                *GetName(), 
                FDateTime::Now().GetTicks());
            
            Manager->TrackItemState(ItemUID, Item.CurrentDurability, Item.MaxDurability, true);
        }
    }

    // Continue with normal container breaking
    Super::OnContainerBroken();
}

Implementation Notes

  • • Tracks item durability states when containers are broken
  • • Forces immediate persistence for container items to prevent exploit
  • • Auto-saves every 2 seconds for real-time durability updates
  • • Cleans up expired item states after 24 hours
  • • Hooks into existing save/load systems (implementation required)
0