diff --git a/src/client/thingtype.cpp b/src/client/thingtype.cpp index 0c211f78ce..8cfb84be0f 100644 --- a/src/client/thingtype.cpp +++ b/src/client/thingtype.cpp @@ -639,6 +639,8 @@ TexturePtr ThingType::getTexture(int animationPhase) { if (m_null) return m_textureNull; + m_lastTimeUsage.restart(); + auto& textureData = m_textureData[animationPhase]; auto& animationPhaseTexture = textureData.source; diff --git a/src/client/thingtype.h b/src/client/thingtype.h index 4330b3c153..be4860015e 100644 --- a/src/client/thingtype.h +++ b/src/client/thingtype.h @@ -368,6 +368,14 @@ class ThingType : public LuaObject bool isMissile() const { return m_category == ThingCategoryMissile; } bool isCreature() const { return m_category == ThingCategoryCreature; } + bool hasTexture() const { return !m_textureData.empty() && m_textureData[0].source != nullptr; } + const Timer getLastTimeUsage() const { return m_lastTimeUsage; } + + void unload() { + m_textureData.clear(); + m_textureData.resize(m_animationPhases); + } + PLAYER_ACTION getDefaultAction() { return m_defaultAction; } uint16_t getClassification() { return m_upgradeClassification; } @@ -446,4 +454,6 @@ class ThingType : public LuaObject std::vector m_textureData; std::atomic_bool m_loading; + + Timer m_lastTimeUsage; }; diff --git a/src/client/thingtypemanager.cpp b/src/client/thingtypemanager.cpp index de5cb2464b..ca57fdb354 100644 --- a/src/client/thingtypemanager.cpp +++ b/src/client/thingtypemanager.cpp @@ -31,6 +31,7 @@ #include #endif +#include #include #include #include @@ -57,6 +58,35 @@ void ThingTypeManager::init() m_nullItemType = std::make_shared(); m_itemTypes.resize(1, m_nullItemType); #endif + + // Garbage Collection + { + constexpr uint16_t + WAITING_TIME = 2 * 1000, // waiting time for next check, default 2 seconds. + TICKET_ELAPSED = 60 * 1000, // Maximum time it can be idle, default 60 seconds. + AMOUNT_PER_CHECK = 500; // maximum number of objects to be checked. + + m_gc.event = g_dispatcher.cycleEvent([&] { + if (m_gc.category == ThingLastCategory) + m_gc.category = ThingCategoryItem; + + const auto& category = m_thingTypes[m_gc.category]; + + const size_t limit = std::min(m_gc.index + AMOUNT_PER_CHECK, category.size()); + while (m_gc.index < limit) { + auto& thing = category[m_gc.index]; + if (thing->hasTexture() && thing->getLastTimeUsage().ticksElapsed() > TICKET_ELAPSED) { + thing->unload(); + } + ++m_gc.index; + } + + if (limit == category.size()) { + m_gc.index = 0; + ++m_gc.category; + } + }, WAITING_TIME); + } } void ThingTypeManager::terminate() @@ -66,6 +96,11 @@ void ThingTypeManager::terminate() m_nullThingType = nullptr; + if (m_gc.event) { + m_gc.event->cancel(); + m_gc.event = nullptr; + } + #ifdef FRAMEWORK_EDITOR m_itemTypes.clear(); m_reverseItemTypes.clear(); diff --git a/src/client/thingtypemanager.h b/src/client/thingtypemanager.h index d9254565cb..c7afb4982c 100644 --- a/src/client/thingtypemanager.h +++ b/src/client/thingtypemanager.h @@ -77,6 +77,13 @@ class ThingTypeManager bool isValidDatId(uint16_t id, ThingCategory category) const { return id >= 1 && id < m_thingTypes[category].size(); } private: + struct GarbageCollection + { + uint8_t category{ ThingLastCategory }; + size_t index; + ScheduledEventPtr event; + }; + ThingTypeList m_thingTypes[ThingLastCategory]; ThingTypePtr m_nullThingType; @@ -86,6 +93,8 @@ class ThingTypeManager uint32_t m_datSignature{ 0 }; uint16_t m_contentRevision{ 0 }; + GarbageCollection m_gc; + #ifdef FRAMEWORK_EDITOR ItemTypePtr m_nullItemType; ItemTypeList m_reverseItemTypes;