ff_u_store_gc

Store garbage collector.

Destroys items that have been in a store for longer than GC_STORE_DURATION hours. Items with the infinite flag are neither tracked nor destroyed.

Items are stamped when sold to the store, and checked when the store is opened. The engine does not run store events through on_event, so ON_DEFAULT_STORE_OPENED is signalled by ff_c_act_store: stores opened by other scripts are not checked.

Variables

ENABLE__FF_U_STORE_GC: (string) (opt-out) Set to "N" on module to disable the unit.

GARBAGE_COLLECT: (string) Set to "Y" on the store to enable garbage collection.

GC_STORE_DURATION: (int) Maximum age, in real-time hours, before an item is destroyed. Read from the store, its area, then the module. Defaults to 168 (7 days), at least 1.

Source code

// @code

#include "ff_i_core"



const string ENABLE__FF_U_STORE_GC = "ENABLE__FF_U_STORE_GC";

const string FF_U_GC_STORE__TIMESTAMP = "__FF_U_GC_STORE__TIMESTAMP";

const string GC_STORE_DURATION = "GC_STORE_DURATION";

const int DEFAULT_DURATION = 7 * 24; // 7 realtime days, in hours

const int SECONDS_PER_HOUR = 3600;

const string GARBAGE_COLLECT = "GARBAGE_COLLECT";


// -----------------------------------------------------------------------------

//! @brief Return TRUE if oObject is a store with the GARBAGE_COLLECT flag set.
//! @param oObject An object.
int IsEligible(object oObject);
int IsEligible(object oObject)
{
  if (GetObjectType(oObject) != OBJECT_TYPE_STORE)
    return FALSE;

  return GetObjectFlag(oObject, GARBAGE_COLLECT);
}


// -----------------------------------------------------------------------------

void _UntrackInfinite(object oStore)
{
  object oItem = GetFirstItemInInventory(oStore);
  while (GetIsObjectValid(oItem))
  {
    if (GetInfiniteFlag(oItem) && GetLocalInt(oItem, FF_U_GC_STORE__TIMESTAMP))
    {
      LogDebug(__FILE__, "Untracking infinite item " + GetObjectInfo(oItem) + " in " + GetTag(oStore));
      DeleteLocalInt(oItem, FF_U_GC_STORE__TIMESTAMP);
    }
    oItem = GetNextItemInInventory(oStore);
  }
}


// -----------------------------------------------------------------------------

void _LogItems(object oStore, string sAction)
{
  int nNow = GetTimeStamp();
  object oItem = GetFirstItemInInventory(oStore);
  while (GetIsObjectValid(oItem))
  {
    if (GetInfiniteFlag(oItem))
    {
      oItem = GetNextItemInInventory(oStore);
      continue;
    }

    int nStamp = GetLocalInt(oItem, FF_U_GC_STORE__TIMESTAMP);
    LogDebug(__FILE__, sAction + " item " + GetObjectInfo(oItem)
      + " in " + GetTag(oStore)
      + " stamp=" + IntToString(nStamp)
      + " age=" + IntToString(nStamp ? nNow - nStamp : 0));
    oItem = GetNextItemInInventory(oStore);
  }
}


// -----------------------------------------------------------------------------

//! @brief Destroy items in oStore that have exceeded GC_STORE_DURATION hours.
//! Infinite items are skipped by DeleteGCItems().
//! @param oStore An eligible store.
void DeleteItems(object oStore);
void DeleteItems(object oStore)
{
  LogDebug(__FILE__, "opened " + GetTag(oStore) + " eligible=" + IntToString(IsEligible(oStore)));
  if (!IsEligible(oStore))
    return;

  _UntrackInfinite(oStore);
  _LogItems(oStore, "Checked");
  DeleteGCItems(oStore, FF_U_GC_STORE__TIMESTAMP, GC_STORE_DURATION, DEFAULT_DURATION, SECONDS_PER_HOUR);
}


// -----------------------------------------------------------------------------

//! @brief Stamp a GC timestamp on every untracked item in oStore.
//! Infinite items are skipped by TrackGCInventory().
//! @param oStore An eligible store.
void TrackItems(object oStore);
void TrackItems(object oStore)
{
  LogDebug(__FILE__, "sold to " + GetTag(oStore) + " eligible=" + IntToString(IsEligible(oStore)));
  if (!IsEligible(oStore))
    return;

  TrackGCInventory(oStore, FF_U_GC_STORE__TIMESTAMP);
  _UntrackInfinite(oStore);
  _LogItems(oStore, "Tracked");
}


// -----------------------------------------------------------------------------

void main()
{
  string sEvent = NWNX_Events_GetCurrentEvent();
  if (sEvent == ON_REGISTER)
  {
    if (!GetModuleFlag(ENABLE__FF_U_STORE_GC, TRUE))
      return;

    SubscribeToEvent(ON_DEFAULT_STORE_OPENED, __FILE__);
    SubscribeToEvent(NWNX_ON_STORE_REQUEST_SELL_AFTER, __FILE__);
    SubscribeToEvent(ON_DELAYED_REGISTER, __FILE__);
  }
  // Avoid ON_DEFAULT_ITEM_ACQUIRE events during startup
  else if (sEvent == ON_DELAYED_REGISTER)
  {
    SubscribeToEvent(ON_DEFAULT_ITEM_ACQUIRE, __FILE__);
  }
  else if (sEvent == ON_DEFAULT_ITEM_ACQUIRE)
    UntrackGCObject(GetModuleItemAcquired(), FF_U_GC_STORE__TIMESTAMP);
  // OBJECT_SELF is the seller
  else if (sEvent == NWNX_ON_STORE_REQUEST_SELL_AFTER)
  {
    if (StringToInt(NWNX_Events_GetEventData("RESULT")))
      TrackItems(StringToObject(NWNX_Events_GetEventData("STORE")));
  }
  else if (sEvent == ON_DEFAULT_STORE_OPENED)
    DeleteItems(OBJECT_SELF);
}