On GitHub there are suggestions that on Real Tibia there are items that decay when player is offline:
Items in depot don’t decay · Issue #2641 · otland/forgottenserver (https://github.com/otland/forgottenserver/issues/2641)
Also items in player depot are not decaying on TFS, until you move it. It should be fixed too.
I think it's time to rewrite items decay.
My idea is to make it timestamp based. I did not benchmark it yet, but it looks like it may work as fast as current implementation.
It will also let us create items that decrease duration even when player is offline.
I wrote little prototype to demonstrate this idea:
Items in depot don’t decay · Issue #2641 · otland/forgottenserver (https://github.com/otland/forgottenserver/issues/2641)
Also items in player depot are not decaying on TFS, until you move it. It should be fixed too.
I think it's time to rewrite items decay.
My idea is to make it timestamp based. I did not benchmark it yet, but it looks like it may work as fast as current implementation.
It will also let us create items that decrease duration even when player is offline.
I wrote little prototype to demonstrate this idea:
C++:
#include <iostream>
#include <list>
#include <map>
#include <chrono>
int64_t OTSYS_TIME()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
class Item;
void internalDecay(Item* item) {
// decays items
}
#define ITEM_ATTRIBUTE_DECAY_TO_TIMESTAMP 123
class Item
{
public:
// for code that shows duration of item to player
int32_t getDurationLeft()
{
return OTSYS_TIME() - getDecayToTimestamp();
}
int32_t getDuration()
{
return duration;
}
void setDuration(int32_t newDuration)
{
duration = newDuration;
}
int64_t getDecayToTimestamp()
{
return decayToTimestamp;
}
void setDecayToTimestamp(int64_t newDecayToTimestamp)
{
decayToTimestamp = newDecayToTimestamp;
}
void removeAttribute(int attributeType) {
}
void decrementReferenceCounter() {
}
void incrementReferenceCounter() {
}
private:
int32_t duration = 123;
// THIS WOULD BE ITEM ATTRIBUTE, no extra RAM usage for items without decay
// it will be savable for items 'decaying offline'
int64_t decayToTimestamp = 0;
};
class DecayList
{
public:
void startDecay(Item *item);
void stopDecay(Item *item);
void decayItems();
private:
// inner map is HashTree C++ implementation
std::map<int64_t, std::map<Item *, Item *>> decayMap;
};
void DecayList::startDecay(Item *item)
{
int64_t decayToTimestamp = OTSYS_TIME() + item->getDuration();
// required to fast remove items and calculate duration left
item->setDecayToTimestamp(decayToTimestamp);
auto decayItemsMap = decayMap[decayToTimestamp];
decayItemsMap[item] = item;
item->incrementReferenceCounter();
}
void DecayList::stopDecay(Item *item)
{
auto decayToTimestamp = item->getDecayToTimestamp();
auto decayItemsMap = decayMap[decayToTimestamp];
decayItemsMap.erase(item);
item->decrementReferenceCounter();
auto durationLeft = item->getDurationLeft();
item->setDuration(durationLeft);
item->removeAttribute(ITEM_ATTRIBUTE_DECAY_TO_TIMESTAMP);
}
void DecayList::decayItems()
{
// g_scheduler.addEvent(createSchedulerTask(50, std::bind(&DecayList::decayItems, this)));
std::list<Item*> itemsToDecay;
auto it = decayMap.begin(), end = decayMap.end();
while (it != end) {
if (it->first > OTSYS_TIME()) {
break;
}
for(auto it2 : it->second) {
itemsToDecay.push_back(it2.first);
}
// is it required?
it->second.clear();
it = decayMap.erase(it);
}
for(auto item : itemsToDecay) {
internalDecay(item);
item->decrementReferenceCounter();
}
}