Files
inventory-system-plugin/Source/ItemInventory/Private/ItemTransaction.cpp
Bonchellon 7f7e043a88 Initial commit: Item Interaction Ecosystem plugin (UE5.7)
Server-authoritative, data-driven item/inventory/interaction/carry/equipment/
world-slot/placement/crafting framework. 8 modules, compiles against UE 5.7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:49:56 +03:00

174 lines
4.4 KiB
C++

// Copyright ExByte Studios. Item Interaction Ecosystem.
#include "ItemTransaction.h"
#include "ItemInventoryLog.h"
#include "InventoryComponent.h"
FItemTransaction& FItemTransaction::Add(UInventoryComponent* Inventory, const FItemInstanceData& Item)
{
FItemTransactionStep Step;
Step.Type = EItemTransactionStepType::AddItem;
Step.Inventory = Inventory;
Step.Item = Item;
Step.Item.EnsureInstanceId();
Step.InstanceId = Step.Item.InstanceId;
Steps.Add(Step);
return *this;
}
FItemTransaction& FItemTransaction::Remove(UInventoryComponent* Inventory, const FGuid& InstanceId)
{
FItemTransactionStep Step;
Step.Type = EItemTransactionStepType::RemoveItem;
Step.Inventory = Inventory;
Step.InstanceId = InstanceId;
Steps.Add(Step);
return *this;
}
FItemTransaction& FItemTransaction::Update(UInventoryComponent* Inventory, const FItemInstanceData& Item)
{
FItemTransactionStep Step;
Step.Type = EItemTransactionStepType::UpdateItem;
Step.Inventory = Inventory;
Step.Item = Item;
Step.InstanceId = Item.InstanceId;
Steps.Add(Step);
return *this;
}
FItemTransaction& FItemTransaction::Move(UInventoryComponent* Inventory, const FGuid& InstanceId, const FGuid& ToContainerId, int32 ToSlotIndex)
{
FItemTransactionStep Step;
Step.Type = EItemTransactionStepType::MoveItem;
Step.Inventory = Inventory;
Step.InstanceId = InstanceId;
Step.ToContainer = ToContainerId;
Step.ToSlot = ToSlotIndex;
Steps.Add(Step);
return *this;
}
bool FItemTransaction::Validate() const
{
for (const FItemTransactionStep& Step : Steps)
{
UInventoryComponent* Inv = Step.Inventory.Get();
if (!Inv)
{
return false;
}
switch (Step.Type)
{
case EItemTransactionStepType::AddItem:
{
// At least one container must accept it.
TArray<FInventoryContainer> Containers;
Inv->GetContainers(Containers);
bool bAccepts = false;
for (const FInventoryContainer& C : Containers)
{
if (Inv->CanAcceptItem(Step.Item, C.ContainerId)) { bAccepts = true; break; }
}
if (!bAccepts) { return false; }
break;
}
case EItemTransactionStepType::RemoveItem:
case EItemTransactionStepType::UpdateItem:
case EItemTransactionStepType::MoveItem:
{
FItemInstanceData Found;
if (!Inv->FindItem(Step.InstanceId, Found) || Found.Lock.bLocked) { return false; }
break;
}
}
}
return true;
}
bool FItemTransaction::Apply()
{
if (!Validate())
{
return false;
}
for (FItemTransactionStep& Step : Steps)
{
UInventoryComponent* Inv = Step.Inventory.Get();
bool bOk = false;
switch (Step.Type)
{
case EItemTransactionStepType::AddItem:
{
FItemInstanceData ToAdd = Step.Item;
bOk = Inv->AddItem(ToAdd);
break;
}
case EItemTransactionStepType::RemoveItem:
{
Inv->FindItem(Step.InstanceId, Step.UndoSnapshot); // capture for rollback
bOk = Inv->RemoveItem(Step.InstanceId);
break;
}
case EItemTransactionStepType::UpdateItem:
{
Inv->FindItem(Step.InstanceId, Step.UndoSnapshot);
bOk = Inv->UpdateItem(Step.Item);
break;
}
case EItemTransactionStepType::MoveItem:
{
Inv->FindItem(Step.InstanceId, Step.UndoSnapshot);
Step.UndoFromContainer = Step.UndoSnapshot.CurrentContainerId;
Step.UndoFromSlot = Step.UndoSnapshot.SlotIndex;
bOk = Inv->MoveItem(Step.InstanceId, Step.UndoFromContainer, Step.ToContainer, Step.ToSlot);
break;
}
}
if (!bOk)
{
UE_LOG(LogItemInventory, Warning, TEXT("[Transaction %s] step failed; rolling back."), *TransactionId.ToString());
Rollback();
return false;
}
Step.bApplied = true;
}
return true;
}
void FItemTransaction::Rollback()
{
// Undo applied steps in reverse order.
for (int32 i = Steps.Num() - 1; i >= 0; --i)
{
FItemTransactionStep& Step = Steps[i];
if (!Step.bApplied)
{
continue;
}
UInventoryComponent* Inv = Step.Inventory.Get();
if (!Inv)
{
continue;
}
switch (Step.Type)
{
case EItemTransactionStepType::AddItem:
Inv->RemoveItem(Step.InstanceId);
break;
case EItemTransactionStepType::RemoveItem:
if (Step.UndoSnapshot.IsValid()) { FItemInstanceData Restore = Step.UndoSnapshot; Inv->AddItem(Restore); }
break;
case EItemTransactionStepType::UpdateItem:
if (Step.UndoSnapshot.IsValid()) { Inv->UpdateItem(Step.UndoSnapshot); }
break;
case EItemTransactionStepType::MoveItem:
Inv->MoveItem(Step.InstanceId, Step.ToContainer, Step.UndoFromContainer, Step.UndoFromSlot);
break;
}
Step.bApplied = false;
}
}