Skip to content

Commit

Permalink
otland#2641 optimized item decay algorithm, timestamp (offline) decay
Browse files Browse the repository at this point in the history
  • Loading branch information
gesior committed Jan 29, 2023
1 parent 6d71dd5 commit c6865c8
Show file tree
Hide file tree
Showing 15 changed files with 287 additions and 134 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -9,6 +9,7 @@
*.suo
*.user
*.sln.docstates
.idea
.vscode/

# Build results
Expand All @@ -19,6 +20,7 @@ x64/
build/
[Bb]in/
[Oo]bj/
cmake-build-*/

# MSTest test Results
[Tt]est[Rr]esult*/
Expand Down
9 changes: 9 additions & 0 deletions src/container.cpp
Expand Up @@ -681,11 +681,20 @@ void Container::internalAddThing(uint32_t, Thing* thing)

void Container::startDecaying()
{
Item::startDecaying();
for (Item* item : itemlist) {
item->startDecaying();
}
}

void Container::stopDecaying()
{
Item::stopDecaying();
for (Item* item : itemlist) {
item->stopDecaying();
}
}

ContainerIterator Container::iterator() const
{
ContainerIterator cit;
Expand Down
1 change: 1 addition & 0 deletions src/container.h
Expand Up @@ -147,6 +147,7 @@ class Container : public Item, public Cylinder
void internalAddThing(Thing* thing) override final;
void internalAddThing(uint32_t index, Thing* thing) override final;
void startDecaying() override final;
void stopDecaying() override final;

protected:
ItemDeque itemlist;
Expand Down
5 changes: 5 additions & 0 deletions src/cylinder.cpp
Expand Up @@ -67,3 +67,8 @@ void Cylinder::startDecaying()
{
//
}

void Cylinder::stopDecaying()
{
//
}
1 change: 1 addition & 0 deletions src/cylinder.h
Expand Up @@ -203,6 +203,7 @@ class Cylinder : virtual public Thing
virtual void internalAddThing(uint32_t index, Thing* thing);

virtual void startDecaying();
virtual void stopDecaying();
};

class VirtualCylinder final : public Cylinder
Expand Down
8 changes: 7 additions & 1 deletion src/enums.h
Expand Up @@ -84,15 +84,21 @@ enum itemAttrTypes : uint32_t {
ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 15,
ITEM_ATTRIBUTE_OWNER = 1 << 16,
ITEM_ATTRIBUTE_DURATION = 1 << 17,
ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18,
//ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18,
ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 19,
ITEM_ATTRIBUTE_CHARGES = 1 << 20,
ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 21,
ITEM_ATTRIBUTE_DOORID = 1 << 22,
ITEM_ATTRIBUTE_DECAY_TIMESTAMP = 1 << 23,

ITEM_ATTRIBUTE_CUSTOM = 1U << 31
};

enum ItemDecayType_t : uint8_t {
DECAY_TYPE_NORMAL = 0,
DECAY_TYPE_TIMESTAMP = 1,
};

enum VipStatus_t : uint8_t {
VIPSTATUS_OFFLINE = 0,
VIPSTATUS_ONLINE = 1,
Expand Down
125 changes: 62 additions & 63 deletions src/game.cpp
Expand Up @@ -1212,12 +1212,8 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder,
return retMaxCount;
}

if (moveItem && moveItem->getDuration() > 0) {
if (moveItem->getDecaying() != DECAYING_TRUE) {
moveItem->incrementReferenceCounter();
moveItem->setDecaying(DECAYING_TRUE);
g_game.toDecayItems.push_front(moveItem);
}
if (moveItem && moveItem->getDurationLeft() > 0) {
startDecay(moveItem);
}

return ret;
Expand Down Expand Up @@ -1304,10 +1300,8 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde
}
}

if (item->getDuration() > 0) {
item->incrementReferenceCounter();
item->setDecaying(DECAYING_TRUE);
g_game.toDecayItems.push_front(item);
if (item->getDurationLeft() > 0) {
startDecay(item);
}

return RETURNVALUE_NOERROR;
Expand Down Expand Up @@ -1351,7 +1345,7 @@ ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool te
if (item->isRemoved()) {
item->onRemoved();
if (item->canDecay()) {
decayItems->remove(item);
stopDecay(item);
}
ReleaseItem(item);
}
Expand Down Expand Up @@ -1682,12 +1676,9 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/)
cylinder->postRemoveNotification(item, cylinder, itemIndex);
ReleaseItem(item);

if (newItem->getDuration() > 0) {
if (newItem->getDecaying() != DECAYING_TRUE) {
newItem->incrementReferenceCounter();
newItem->setDecaying(DECAYING_TRUE);
g_game.toDecayItems.push_front(newItem);
}
stopDecay(item);
if (newItem->getDurationLeft() > 0) {
startDecay(newItem);
}

return newItem;
Expand Down Expand Up @@ -4393,24 +4384,55 @@ void Game::addDistanceEffect(const SpectatorHashSet& spectators, const Position&
}
}

void Game::startDecay(Item* item)
bool Game::isDecaying(Item *item)
{
if (!item) {
return false;
}

return reverseItemDecayMap.find(item) != reverseItemDecayMap.end();
}

void Game::startDecay(Item *item)
{
if (!item || !item->canDecay()) {
return;
}

ItemDecayState_t decayState = item->getDecaying();
if (decayState == DECAYING_TRUE) {
if (isDecaying(item)) {
return;
}

if (item->getDuration() > 0) {
item->incrementReferenceCounter();
item->setDecaying(DECAYING_TRUE);
toDecayItems.push_front(item);
} else {
internalDecayItem(item);
int64_t decayToTimestamp = OTSYS_TIME() + item->getDurationLeft();

if (item->getDecayType() == DECAY_TYPE_NORMAL) {
item->setDecayTimestamp(decayToTimestamp);
}

decayMap[decayToTimestamp][item] = item;
reverseItemDecayMap[item] = decayToTimestamp;
item->incrementReferenceCounter();
}

void Game::stopDecay(Item *item)
{
if (!item) {
return;
}

auto it = reverseItemDecayMap.find(item);
if (it == reverseItemDecayMap.end()) {
return;
}

if (item->getDecayType() == DECAY_TYPE_NORMAL) {
item->setDuration(item->getDurationLeft());
item->removeAttribute(ITEM_ATTRIBUTE_DECAY_TIMESTAMP);
}

reverseItemDecayMap.erase(it);
decayMap[it->second].erase(item);
ReleaseItem(item);
}

void Game::internalDecayItem(Item* item)
Expand All @@ -4431,43 +4453,30 @@ void Game::checkDecay()
{
g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, std::bind(&Game::checkDecay, this)));

size_t bucket = (lastBucket + 1) % EVENT_DECAY_BUCKETS;
int64_t time = OTSYS_TIME();

auto it = decayItems[bucket].begin(), end = decayItems[bucket].end();
std::list<Item*> itemsToDecay;

auto it = decayMap.begin(), end = decayMap.end();
while (it != end) {
Item* item = *it;
if (!item->canDecay()) {
item->setDecaying(DECAYING_FALSE);
ReleaseItem(item);
it = decayItems[bucket].erase(it);
continue;
if (it->first > time) {
break;
}

int32_t duration = item->getDuration();
int32_t decreaseTime = std::min<int32_t>(EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS, duration);
for(auto it2 : it->second) {
itemsToDecay.push_back(it2.first);
}

duration -= decreaseTime;
item->decreaseDuration(decreaseTime);
it = decayMap.erase(it);
}

if (duration <= 0) {
it = decayItems[bucket].erase(it);
for(auto item : itemsToDecay) {
stopDecay(item);
if (item->canDecay()) {
internalDecayItem(item);
ReleaseItem(item);
} else if (duration < EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) {
it = decayItems[bucket].erase(it);
size_t newBucket = (bucket + ((duration + EVENT_DECAYINTERVAL / 2) / 1000)) % EVENT_DECAY_BUCKETS;
if (newBucket == bucket) {
internalDecayItem(item);
ReleaseItem(item);
} else {
decayItems[newBucket].push_back(item);
}
} else {
++it;
}
}

lastBucket = bucket;
cleanup();
}

Expand Down Expand Up @@ -4562,16 +4571,6 @@ void Game::cleanup()
item->decrementReferenceCounter();
}
ToReleaseItems.clear();

for (Item* item : toDecayItems) {
const uint32_t dur = item->getDuration();
if (dur >= EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) {
decayItems[lastBucket].push_back(item);
} else {
decayItems[(lastBucket + 1 + dur / 1000) % EVENT_DECAY_BUCKETS].push_back(item);
}
}
toDecayItems.clear();
}

void Game::ReleaseCreature(Creature* creature)
Expand Down
12 changes: 5 additions & 7 deletions src/game.h
Expand Up @@ -71,8 +71,7 @@ enum LightState_t {
};

static constexpr int32_t EVENT_LIGHTINTERVAL = 10000;
static constexpr int32_t EVENT_DECAYINTERVAL = 250;
static constexpr int32_t EVENT_DECAY_BUCKETS = 4;
static constexpr int32_t EVENT_DECAYINTERVAL = 50;

/**
* Main Game class.
Expand Down Expand Up @@ -452,6 +451,8 @@ class Game
static void addDistanceEffect(const SpectatorHashSet& spectators, const Position& fromPos, const Position& toPos, uint8_t effect);

void startDecay(Item* item);
void stopDecay(Item* item);
bool isDecaying(Item* item);
int32_t getLightHour() const {
return lightHour;
}
Expand Down Expand Up @@ -504,8 +505,6 @@ class Game
Raids raids;
Quests quests;

std::forward_list<Item*> toDecayItems;

private:
bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text);
void playerWhisper(Player* player, const std::string& text);
Expand All @@ -522,14 +521,13 @@ class Game
std::unordered_map<uint16_t, Item*> uniqueItems;
std::map<uint32_t, uint32_t> stages;

std::list<Item*> decayItems[EVENT_DECAY_BUCKETS];
std::map<int64_t, std::map<Item *, Item *>> decayMap;
std::map<Item *, int64_t> reverseItemDecayMap;
std::list<Creature*> checkCreatureLists[EVENT_CREATURECOUNT];

std::vector<Creature*> ToReleaseCreatures;
std::vector<Item*> ToReleaseItems;

size_t lastBucket = 0;

WildcardTreeNode wildcardTree { false };

std::map<uint32_t, Npc*> npcs;
Expand Down

0 comments on commit c6865c8

Please sign in to comment.