Cleanup CGameItemPtr, add reference count to avoid glitches, kaetemi/ryzomclassic#194

ryzomclassic-develop
kaetemi 4 years ago
parent 66debd7ebe
commit 9eb7481c06

@ -256,16 +256,12 @@ private:
void keepTheThreeBestResistance( const CCraftParameters& p );
};
//------------------------------------------------------------------------
// advance declaration of classes with circular references
class CGameItem;
class CGameItemEntry;
//class CGameItemManager;
/**
* CGameItemVector
*
@ -278,7 +274,7 @@ class CGameItemVector
{
public:
uint32 size() const;
CGameItem& operator[](uint32 idx);
CGameItemEntry& operator[](uint32 idx);
void extend();
uint32 getUniqueIndex(const CGameItem& item);
virtual ~CGameItemVector();
@ -289,45 +285,9 @@ private:
CGameItemVector();
private:
std::vector< CGameItem* > _Data;
};
#ifdef GAME_PTR_DEBUG
class CGameItemPtrArray
{
public:
CGameItemPtrArray()
{
}
CGameItemPtrArray(const CGameItemPtrArray&)
{
// do nothing
}
CGameItemPtrArray &operator=(const CGameItemPtrArray &)
{
// do nothing
}
#ifdef NL_CPP14
CGameItemPtrArray(CGameItemPtrArray &&)
{
// do nothing
}
CGameItemPtrArray &operator=(CGameItemPtrArray &&) noexcept
{
// do nothing
}
#endif
std::vector<CGameItemEntry *> m_Data;
protected:
/// keep pointers pointing this item
std::vector<CGameItemPtr*> _Ptrs;
};
#endif
/**
* CGameItem
@ -337,16 +297,17 @@ protected:
* \date 2002
*/
class CGameItem
#ifdef GAME_PTR_DEBUG
: public CGameItemPtrArray
#endif
{
#if 0
NLMISC_COMMAND_FRIEND(testParanoidItemSystem);
#endif
friend class CInventoryBase;
friend class CRefInventory;
#if 0
NL_INSTANCE_COUNTER_DECL(CGameItem);
#endif
public:
/// returns true if the two param items are stackable
@ -752,10 +713,10 @@ protected:
/// The inventory that contains this item (NULL if none)
CInventoryPtr _Inventory;
/// The slot inside the inventory
uint32 _InventorySlot;
/// The inventory that reference this item (NULL if none)
CInventoryPtr _RefInventory;
/// The slot inside the inventory
uint32 _InventorySlot;
/// The slot inside the reference inventory
uint32 _RefInventorySlot;
@ -773,20 +734,12 @@ private:
static CGameItemVector _Items;
static uint32 _FirstFreeItem;
#ifdef ITEM_DEBUG
static sint32 _NextAllocatorDataValue;
#endif
//--------------------------------------------------------------------
// instance data for singleton's allocator algorithms
sint32 _AllocatorData;
private:
//--------------------------------------------------------------------
// singleton interface used by the CGameItemPtr class
// make CGameItemPtr a friend to give it decent access
friend class CGameItemEntry;
friend class CGameItemPtr;
friend class CInventoryProxy;
friend class COldGuildInventoryLoader;
@ -807,32 +760,10 @@ private:
// return the CWeaponCraftParameters variable corresponding to protection type
float getMagicProtectionCraftParateters( PROTECTION_TYPE::TProtectionType protection ) const;
private:
#if 0
// singleton counter
static uint32 _BugTestCounter;
// instance debug data
uint32 _BugTestChecksum;
uint32 _BugTestUpdate;
#endif
public:
#if 0
// run through the items looking for bugs
static void testItemsForBugs();
// static void testPlayerInventoryForBugs(const std::vector<CGameItemPtr>& inventory);
#endif
static std::string showItemsStats();
#if 0
// check a single item for bugs...
void checkItemForBugs();
#endif
public: // I've had to make these public for now 'cos I can't work out how to make the vector class a friend :o(
//private :
private :
//--------------------------------------------------------------------
// ctors and dtors are now private for better control over allocation etc
@ -877,7 +808,7 @@ private:
/**
* pseudo Destructor
*/
void dtor();
//void dtor();
/// Copy all the item info and parameters from model into this
// void copyItem(const CGameItemPtr &model);
@ -921,22 +852,16 @@ public:
private:
/// Item unique id
INVENTORIES::TItemId _ItemId;
/// Item stack size
uint32 _StackSize;
// true if the item is on the ground
// bool _IsOnTheGround;
/// item id
// NLMISC::CEntityId _ItemId;
/// looter id of the character currently looting this item (for items on the ground only)
NLMISC::CEntityId _Looter;
/// sheet ref
NLMISC::CSheetId _SheetId;
/// Item stack size
uint32 _StackSize;
/// carrion sheet ref
// uint32 _CarrionSheetId;
/// true if the object is destroyable
bool _Destroyable;
/// true if the object is dropable
bool _Dropable;
/// children
// std::vector<CGameItemPtr> _Children;
/// pointer on the parent item (NULL if no parent)
@ -954,52 +879,64 @@ private:
/// current sap load
uint32 _SapLoad;
/// all craft parameters
NLMISC::CDeepPtr<CItemCraftParameters> _CraftParameters;
/// entityId of the character who has created the Item via faber (if applicable, for item not created by playres, Creator = CEntityId::Unknown)
NLMISC::CEntityId _CreatorId;
/// number of item locked
uint32 _LockCount;
/// Position in the client inventory interface if item is owned by a player (sint16 juste because we need an "invalid" position (-1) for version compatibility)
// TODO : a virer
sint16 _ClientInventoryPosition;
/// Vector of CSheetId used for craft this item
// std::vector< NLMISC::CSheetId > _RmUsedForCraft;
/// vector of sheetId of CStaticBrick define spell of enchanted item
std::vector< NLMISC::CSheetId > _Enchantment;
/// pointer on the associated static form
const CStaticItem* _Form;
/// string associated with this item
NLMISC::CDeepPtr<std::string> _PhraseId;
/// skill modifiers against given ennemy types
std::vector<CTypeSkillMod> _TypeSkillMods;
NLMISC::CDeepPtr<std::string> _CustomText;
#ifdef RYZOM_FORGE_PET_NAME
// FIXME: Reuse _PhraseId string and add a m_Literal flag
ucstring _CustomName;
#endif
/// tick when the proc will be available again
NLMISC::TGameCycle _LatencyEndDate;
/// image of the item in bag / equipment
// uint16 _SlotImage;
NLMISC::TGameCycle _TotalSaleCycle;
/// number of item locked
uint32 _LockCount;
// required skill
bool _UseNewSystemRequirement;
SKILLS::ESkills _RequiredSkill;
uint16 _RequiredSkillLevel;
SKILLS::ESkills _RequiredSkill2;
CHARACTERISTICS::TCharacteristics _RequiredCharac;
uint16 _RequiredSkillLevel;
uint16 _RequiredSkillLevel2;
/// min required stat level and required stat
CHARACTERISTICS::TCharacteristics _RequiredCharac;
uint16 _RequiredCharacLevel;
/// whether the item has any skill requirements
bool _HasPrerequisit;
/// skill modifiers against given ennemy types
std::vector<CTypeSkillMod> _TypeSkillMods;
bool _UseNewSystemRequirement;
NLMISC::CDeepPtr<std::string> _CustomText;
bool _LockedByOwner;
bool _UnMovable;
bool _Movable;
uint8 _PetIndex;
#ifdef RYZOM_FORGE_PET_NAME
ucstring _CustomName;
#endif
/// true if the object is destroyable
bool _Destroyable;
/// true if the object is dropable
bool _Dropable;
// true if the item is on the ground
// bool _IsOnTheGround;
};
/**
@ -1029,110 +966,128 @@ private:
#define QUANTUM (1<<LOG_QUANTUM)
#define QUANTUM_MASK (QUANTUM-1)
class CGameItemEntry : public CGameItem
{
public:
CGameItemEntry()
{
PtrRefCount = 0;
}
~CGameItemEntry()
{
}
uint32 AllocatorNext;
uint32 VectorIdx;
sint PtrRefCount;
};
inline CGameItemVector::CGameItemVector()
{
}
inline CGameItemVector::~CGameItemVector()
{
for (uint32 i=0;i<_Data.size();++i)
delete [] _Data[i];
for (uint32 i = 0; i < m_Data.size(); ++i)
delete[] m_Data[i];
}
inline uint32 CGameItemVector::size() const
{
return (uint32)_Data.size()<<LOG_QUANTUM;
return (uint32)m_Data.size() << LOG_QUANTUM;
}
inline CGameItem& CGameItemVector::operator[](uint32 idx)
inline CGameItemEntry &CGameItemVector::operator[](uint32 idx)
{
return _Data[idx>>LOG_QUANTUM][idx&QUANTUM_MASK];
return m_Data[idx >> LOG_QUANTUM][idx & QUANTUM_MASK];
}
inline void CGameItemVector::extend()
{
_Data.push_back(new CGameItem[QUANTUM]);
nlassert(_Data.back()!=NULL);
for (uint32 i=0;i<QUANTUM;++i)
_Data.back()[i]._AllocatorData=size()-QUANTUM+i+1;
egs_giinfo("Increased item vector size to %u items (%u bytes)",size(),size()*sizeof(CGameItem));
uint32 baseIdx = size();
CGameItemEntry *items = new CGameItemEntry[QUANTUM];
nlassert(items);
m_Data.push_back(items);
for (uint32 i = 0; i < QUANTUM; ++i)
{
items[i].AllocatorNext = baseIdx + i + 1;
items[i].VectorIdx = baseIdx + i;
}
egs_giinfo("Increased item vector size to %u items (%u bytes)", size(), size() * sizeof(CGameItem));
}
inline uint32 CGameItemVector::getUniqueIndex(const CGameItem& item)
{
for (uint32 i=0;i<_Data.size();++i)
{
uint32 lowidx= (uint32)(&item-_Data[i]);
if (lowidx<QUANTUM)
{
uint32 idx= lowidx+(i<<LOG_QUANTUM);
#ifdef ITEM_DEBUG
BOMB_IF(&(_Data[i][lowidx])!=&item, NLMISC::toString("Invalid item pointer (1): %p",&item), return ~0u );
BOMB_IF(&((*this)[idx])!=&item, NLMISC::toString("Code bug or Invalid item pointer (2): %p",&item), return ~0u );
#endif
return idx;
}
}
STOP(NLMISC::toString("invalid item pointer (3): %p",&item));
return ~0u;
return static_cast<const CGameItemEntry &>(item).VectorIdx;
}
#undef LOG_QUANTUM
#undef QUANTUM
#undef QUANTUM_MASK
//------------------------------------------------------------------------
// ptr class added by sadge
// inline implementations
// ctor - default
inline CGameItemPtr::CGameItemPtr()
// link ptr to game item
inline void CGameItemPtr::incRef()
{
reset();
if (!m_Idx)
return;
++CGameItem::_Items[m_Idx].PtrRefCount;
}
inline void CGameItemPtr::reset()
// unlink ptr from item
inline void CGameItemPtr::decRef()
{
// cannot unlink a pointer pointing on nothing
if (!m_Idx)
return;
CGameItemEntry *entry = &CGameItem::_Items[m_Idx];
sint count = --CGameItem::_Items[m_Idx].PtrRefCount;
if (!count)
{
CGameItem::deleteItem(static_cast<CGameItem *>(entry));
}
nlassert(count >= 0);
}
// ctor - default
inline CGameItemPtr::CGameItemPtr() : m_Idx(0)
{
_idx = ~0u;
#ifdef ITEM_DEBUG
_debugPtr= NULL;
_debug=0;
#endif
}
// ctor - copy
inline CGameItemPtr::CGameItemPtr(const CGameItemPtr &other)
inline CGameItemPtr::CGameItemPtr(const CGameItemPtr &other) : m_Idx(other.m_Idx)
{
reset();
*this=other;
incRef();
}
// ctor - initialise from a CGameItem*
inline CGameItemPtr::CGameItemPtr(const CGameItem *item)
inline CGameItemPtr::CGameItemPtr(const CGameItem *item) : m_Idx(static_cast<const CGameItemEntry *>(item)->VectorIdx)
{
reset();
*this=item;
incRef();
}
// dtor
inline CGameItemPtr::~CGameItemPtr()
{
unlinkFromItem();
decRef();
}
// equivalent to: new CGameItem
inline CGameItem *CGameItemPtr::newItem(bool destroyable,bool dropable)
{
CGameItem *item=CGameItem::newItem();
CGameItem *item = CGameItem::newItem();
item->ctor();
item->_Destroyable=destroyable;
item->_Dropable = dropable;
*this=item;
#if 0
item->_BugTestUpdate=CGameItem::_BugTestCounter;
#endif
return item;
}
@ -1153,93 +1108,17 @@ inline CGameItem *CGameItemPtr::newItem( const NLMISC::CSheetId& sheetId, uint32
// item->ctor(id,sheetId,recommended,slotCount,destroyable,dropable);
item->ctor(sheetId, recommended, destroyable, dropable);
*this=item;
#if 0
item->_BugTestUpdate = CGameItem::_BugTestCounter;
#endif
return item;
}
// equivalent to: delete (for a CGameItem*)
//inline void CGameItemPtr::deleteItem()
//{
// CGameItem *item=**this;
//
// BOMB_IF( item == NULL, "Attempt to delete an item that is not allocated or has been freed", return );
//
// // only unlink if the pointer 'this' is not the same as the inventory ptr for the item
// // after the unlink this == NULL
// if (! ( item->_Parent!=NULL &&
// item->Loc.Slot<item->_Parent->getChildren().size() &&
// &(item->_Parent->getChildren()[item->Loc.Slot])==this ) )
// unlinkFromItem();
//
// // call dtor now to unlink all children
// item->dtor();
// // check no one else is referencing us
// nlassert(item->_Ptrs.empty());
//
// CGameItem::deleteItem(item);
// item->_BugTestUpdate=CGameItem::_BugTestCounter;
//}
// link ptr to game item
inline void CGameItemPtr::linkToItem()
{
#ifdef GAME_PTR_DEBUG
if (_idx == ~0u)
return;
CGameItem *item=**this;
for (uint i = 0 ; i < item->_Ptrs.size() ; ++i)
{
nlassert(item->_Ptrs[i] != this);
}
item->_Ptrs.push_back(this);
#endif
}
// unlink ptr from item
inline void CGameItemPtr::unlinkFromItem()
{
// cannot unlink a pointer pointing on nothing
if (_idx == ~0u)
return;
#ifdef GAME_PTR_DEBUG
CGameItem *item=**this;
uint j=~0u;
for (uint i = 0 ; i < item->_Ptrs.size() ; ++i)
{
if (item->_Ptrs[i] == this)
{
nlassert(j==~0u);
j=i;
}
}
nlassert(j!=~0u);
item->_Ptrs.erase(item->_Ptrs.begin()+j);
#endif
// set the GameItemPtr to NULL
reset();
}
// = operator - for copying another CGameItemPtr
inline const CGameItemPtr &CGameItemPtr::operator=(const CGameItemPtr &other)
{
unlinkFromItem();
decRef();
_idx=other._idx;
#ifdef ITEM_DEBUG
_debug=other._debug;
_debugPtr=other._debugPtr;
#endif
m_Idx = other.m_Idx;
linkToItem();
incRef();
return *this;
}
@ -1247,135 +1126,76 @@ inline const CGameItemPtr &CGameItemPtr::operator=(const CGameItemPtr &other)
// = operator - for assignment of CGameItemPtr directly to a CGameItem
inline const CGameItemPtr &CGameItemPtr::operator=(const CGameItem *item)
{
unlinkFromItem();
decRef();
if (item==NULL)
_idx=~0u;
else
if (!item)
{
_idx=CGameItem::_Items.getUniqueIndex(*item);
// this test would only have value if the implementation of std::vector<> doesn't guarantee a
// continuous memory address space for the vector's data
// It's not in the #IFDEF DEBUG because it may only be triggered in very obscure conditions
if (CGameItem::getItem(_idx)!=item)
{
nlwarning("**** BIG BAD NASTY BUG - BEGINS ****");
nlwarning("item: %p",item);
nlwarning("_idx: %i",_idx);
nlwarning("**** BIG BAD NASTY BUG - ENDS ****");
}
// nlassert(&CGameItem::getItem(_idx)==item);
m_Idx = 0;
}
else
{
m_Idx = static_cast<const CGameItemEntry *>(item)->VectorIdx;
nlassert(CGameItem::getItem(m_Idx) == item);
#ifdef ITEM_DEBUG
_debugPtr= (CGameItem *)item;
if (item==NULL)
_debug=0;
else
{
CGameItem * item = CGameItem::getItem(_idx);
if( item )
_debug=item->_AllocatorData;
}
#endif
linkToItem();
incRef();
}
return *this;
}
// * operator - returning the item referenced by this pointer
inline CGameItem * CGameItemPtr::operator*() const
inline CGameItem *CGameItemPtr::operator*() const
{
if (_idx == ~0u)
return NULL;
CGameItem* item= CGameItem::getItem(_idx);
#ifdef ITEM_DEBUG
BOMB_IF( CGameItem::getItem(_idx) !=_debugPtr, "CGameItemPtr do not reference the good item", return 0 );
BOMB_IF( _idx == ~0u, "Attempting to derefence a NULL pointer", return 0 );
if( item )
BOMB_IF( _debug!=CGameItem::getItem(_idx)->_AllocatorData, "Attempting to access an item that has been re-allocated", return 0 );
#endif
BOMB_IF(item==0, "Attempting to access an item an item that is not allocated or has been freed", return 0);
#if 0
item->_BugTestUpdate=CGameItem::_BugTestCounter;
#endif
return item;
CGameItemEntry *entry = static_cast<CGameItemEntry *>(CGameItem::getItem(m_Idx));
BOMB_IF(!entry->VectorIdx, "Attempting to access an item an item that is not allocated or has been freed", return 0);
return static_cast<CGameItem *>(entry);
}
// -> operator - returning the item referenced by this pointer
inline CGameItem *CGameItemPtr::operator->() const
{
if (_idx == ~0u)
return NULL;
CGameItem* item= CGameItem::getItem(_idx);
#ifdef ITEM_DEBUG
BOMB_IF( CGameItem::getItem(_idx) !=_debugPtr, "CGameItemPtr do not reference the good item", return 0 );
BOMB_IF( _idx == ~0u, "Attempting to derefence a NULL pointer", return 0 );
if( item )
BOMB_IF( _debug!=CGameItem::getItem(_idx)->_AllocatorData, "Attempting to access an item that has been re-allocated", return 0 );
#endif
BOMB_IF(item==0, "Attempting to access an item an item that is not allocated or has been freed", return 0);
#if 0
item->_BugTestUpdate=CGameItem::_BugTestCounter;
#endif
return item;
// this is crash-safe even when m_Idx is wrong, it'll return item 0
return CGameItem::getItem(m_Idx);
}
// () operator - returning the item referenced by this pointer
inline CGameItem *CGameItemPtr::operator()() const
{
return operator->();
CGameItemEntry *entry = static_cast<CGameItemEntry *>(CGameItem::getItem(m_Idx));
BOMB_IF(!entry->VectorIdx, "Attempting to access an item an item that is not allocated or has been freed", return 0);
return static_cast<CGameItem *>(entry);
}
// == operator - compare 2 CGameItemPtrs
inline bool CGameItemPtr::operator==(const CGameItemPtr &other) const
{
#ifdef ITEM_DEBUG
//nlassert(CGameItem::getItem(_idx)==_debugPtr);
if(_idx==other._idx)
nlassert(_debug==other._debug);
#endif
return (_idx==other._idx);
return (m_Idx == other.m_Idx);
}
// == operator - compare CGameItemPtrs to CGameItem*
inline bool CGameItemPtr::operator==(const CGameItem *item) const
{
if (item==NULL)
return operator*()==NULL;
if (!item)
return m_Idx == 0;
return *this==CGameItemPtr(item);
return CGameItem::getItem(m_Idx) == item;
}
// != operator - compare 2 CGameItemPtrs
inline bool CGameItemPtr::operator!=(const CGameItemPtr &other) const
{
return !(*this==other);
return !(*this == other);
}
// != operator - compare CGameItemPtrs to CGameItem*
inline bool CGameItemPtr::operator!=(const CGameItem *item) const
{
if (item==NULL)
return operator*()!=NULL;
return !(*this==item);
return !(*this == item);
}
inline bool CGameItemPtr::operator < (const CGameItemPtr &other) const
{
return _idx < other._idx;
return m_Idx < other.m_Idx;
}
#endif // GAME_ITEM_H

@ -54,7 +54,7 @@ public:
void deleteItem();
// * operator - returning the item referenced by this pointer
CGameItem * operator*() const;
CGameItem *operator*() const;
// -> operator - returning the item referenced by this pointer
CGameItem *operator->() const;
@ -74,27 +74,23 @@ public:
bool operator==(const CGameItem *item) const;
bool operator!=(const CGameItem *item) const;
uint32 getUniqueIndex() { return _idx; }
uint32 getUniqueIndex() { return m_Idx; }
// For set and map
bool operator < (const CGameItemPtr &other) const;
// operator bool() { return m_Idx; }
// bool operator!() { return !m_Idx; }
private:
// link to item
void linkToItem();
void incRef();
// unlink from item
void unlinkFromItem();
// init
void reset();
void decRef();
// index into the CGameItem singleton's _Items vector
uint32 _idx;
#ifdef ITEM_DEBUG
// used for making sure that referenced items haven't been freed or reallocated
// contains a copy of a CGameItem's allocation index (which changes at every alloaction and free)
sint32 _debug;
CGameItem* _debugPtr;
#endif
uint32 m_Idx;
};

Loading…
Cancel
Save