Solution for the item durability exploit in container systems
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
#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
#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
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();
}