• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

attackSpeed enabled = HIGH CPU USAGE

Drucken

Member
Joined
Mar 7, 2023
Messages
45
Solutions
1
Reaction score
15
Engine: TFS 1.5 (Nekiro Downgrade)
Tibia Version: 8.60

Is there any solution for the attack speed issue in TFS 1.5 version 8.60 downgraded by Nekiro? I ask because having the "classicAttackSpeed" boolean activated causes very high CPU usage, which causes lag on the server. When "classicAttackSpeed" is disabled, CPU usage drops by 70%.

The problem seems to come from the "doAttacking" function in the player.cpp file... This is the TFS 1.5 doAttacking:
C++:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();
        bool classicSpeed = g_config.getBoolean(ConfigManager::CLASSIC_ATTACK_SPEED);

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!classicSpeed && !canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }

        SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        if (!classicSpeed) {
            setNextActionTask(task, false);
        } else {
            g_scheduler.addEvent(task);
        }

        if (result) {
            lastAttack = OTSYS_TIME();
        }
    }
}

And here is another type of "doAttacking" from OTX 2 (based on TFS 0.3.7, older version), I'm only putting it here because it's done differently:
C++:
void Player::doAttacking(uint32_t)
{
    uint32_t attackSpeed = getAttackSpeed();
    if(attackSpeed == 0 || (hasCondition(CONDITION_PACIFIED) && !hasCustomFlag(PlayerCustomFlag_IgnorePacification)))
    {
        lastAttack = OTSYS_TIME();
        return;
    }

    if(!lastAttack)
        lastAttack = OTSYS_TIME() - attackSpeed - 1;
    else if((OTSYS_TIME() - lastAttack) < attackSpeed)
        return;

    if(const Weapon* _weapon = g_weapons->getWeapon(weapon))
    {
        if(!g_config.getBool(ConfigManager::CLASSIC_ATTACK_SPEED) && _weapon->interruptSwing() && !canDoAction())
        {
            SchedulerTask* task = createSchedulerTask(getNextActionTime(),
                boost::bind(&Game::checkCreatureAttack, &g_game, getID()));
            setNextActionTask(task);
        }
        else
        {
            if((!_weapon->hasExhaustion() || !hasCondition(CONDITION_EXHAUST)) && _weapon->useWeapon(this, weapon, attackedCreature))
                lastAttack = OTSYS_TIME();

            updateWeapon();
        }
    }
    else if(Weapon::useFist(this, attackedCreature))
        lastAttack = OTSYS_TIME();
}

I have no idea if the attack speed is better processed in OTX 2, I only know that TFS 1.5 does not have a good way of processing attacks, which causes very high CPU usage.

Someone who can help with this please
😪

BUMP
 
Solution
Someone asked me how to fix it.

Problem is not with CPU usage of basic weapon attacks. Problem is that 'classicAttackSpeed' algorithm is bugged and may generate infinite number of scheduler (dispatcher) events.
1. TFS calls Player::doAttacking every second for every player.
2. It creates new event g_scheduler.addEvent(task);, which calls Player::doAttacking again with getAttackSpeed() delay. So there is repeating event every 0.2 sec (with attack speed 200) and TFS still calls Player::doAttacking every second creating new event.
That way server can create infinite number of events for 1 player. 1 new event every second.

Often it all stops on if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) { -...
:S so you know how to handle a language, and now you're mr irony. :S omg . shhhh
thanks you for your release. but do not. simply do not.
Remember that you did not made it up by yourself. lots of ppl helped you if it's not by gesior, peonso and so on you wouldn't even succeed on login in or reading packets.

. You failed because you did not knew how to handle repositories so fucked it up or screw it up with commits and mixed them up.... after it you drop it all( easy. isn't it?)
why can't you be like codex ng or xikini or gesior.
no it's better to feel a demy god
psst: you're not legendary. just nekiro
ps: and yeah it was. at least not using high cpu as tfs 1.x. ban me again as always.
 
Last edited:
:S so you know how to handle a language, and now you're mr irony. :S omg . shhhh
thanks you for your release. but do not. simply do not.
Remember that you did not made it up by yourself. lots of ppl helped you if it's not by gesior, peonso and so on you wouldn't even succeed on login in or reading packets.

. You failed because you did not knew how to handle repositories so fucked it up or screw it up with commits and mixed them up.... after it you drop it all( easy. isn't it?)
why can't you be like codex ng or xikini or gesior.
no it's better to feel a demy god
psst: you're not legendary. just nekiro
ps: and yeah it was. at least not using high cpu as tfs 1.x. ban me again as always.
what? xD
most of the things you said here are complete nonsense

I was just saying I dont think 0.4 was perfect (and had no issues), because 1.x has all these optimizations just, because 0.4 was not performant enough. Why do you think it happened, for no reason?
 
:S so you know how to handle a language, and now you're mr irony. :S omg . shhhh
thanks you for your release. but do not. simply do not.
Remember that you did not made it up by yourself. lots of ppl helped you if it's not by gesior, peonso and so on you wouldn't even succeed on login in or reading packets.

. You failed because you did not knew how to handle repositories so fucked it up or screw it up with commits and mixed them up.... after it you drop it all( easy. isn't it?)
why can't you be like codex ng or xikini or gesior.
no it's better to feel a demy god
psst: you're not legendary. just nekiro
ps: and yeah it was. at least not using high cpu as tfs 1.x. ban me again as always.

I dont even know who nekiro is and I couldnt care less but bro... it seems u got huge butthurt here, you jealous or smth?
 
Sarcasm, thanks, questions, love and help in one post xD
I just want to thank everyone

Nekiro Thanks for tfs 1.5​

Gesior.pl Thanks for help​

Drucken Thanks for the post that made us happy

💖
💖
💖

 
Someone asked me how to fix it.

Problem is not with CPU usage of basic weapon attacks. Problem is that 'classicAttackSpeed' algorithm is bugged and may generate infinite number of scheduler (dispatcher) events.
1. TFS calls Player::doAttacking every second for every player.
2. It creates new event g_scheduler.addEvent(task);, which calls Player::doAttacking again with getAttackSpeed() delay. So there is repeating event every 0.2 sec (with attack speed 200) and TFS still calls Player::doAttacking every second creating new event.
That way server can create infinite number of events for 1 player. 1 new event every second.

Often it all stops on if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) { - player cannot attack as fast as these events execute, so if there is more than 1 dispatcher event for given player, it won't re-create it after execution (addEvent is inside that IF).
But if playeruses melee weapon and his target is not within 1 sqm, his attack will never execute and lastAttack will never update, so it will be able to re-create events, even if there is more than 1 running in same time.

How to reproduce:
1. Add in void Player::doAttacking(uint32_t) (or watch checkCreatureAttack executions per second using OTS Stats dispatcher.log):
C++:
std::cout << OTSYS_TIME() << " doAttacking " << name << std::endl;
2. Turn on classic attack in config.lua
3. Login 2 players few SQM from each other.
4. Attack one player by melee weapon - it will work even with normal attack speed '2000'.
5. Watch how number of doAttacking (or checkCreatureAttack) executions per second grows over time.
You can make it go high faster by running around. Each step of attacked/attacking player adds 1 extra event.

How to fix:
1. In player.h under:
C++:
uint32_t walkTaskEvent = 0;
add:
C++:
uint32_t classicAttackEvent = 0;
2. In player.cpp in function void Player::doAttacking(uint32_t) replace:
C++:
g_scheduler.addEvent(task);
with:
C++:
g_scheduler.stopEvent(classicAttackEvent);
classicAttackEvent = g_scheduler.addEvent(task);
It will limit number of checkCreatureAttack events to 1 per player.

Bug submited on github: classicAttackSpeed makes server go 100% CPU · Issue #4676 · otland/forgottenserver (https://github.com/otland/forgottenserver/issues/4676)
I feel like it did nothing. I might be wrong; maybe it's a small improvement.

Before:
Code:
[11/05/2024 12:54:18]
Thread: 1 Cpu usage: 42.5874% Idle: 56.8241% Other: 0.588483% Players online: 181
 Time (ms)     Calls     Rel usage %    Real usage % Description
      3470     33494       27.16133%       11.56731% std::bind(&Game::checkCreatureAttack, &g_game, getID())

After:
Code:
[11/05/2024 16:20:48]
Thread: 1 Cpu usage: 39.6254% Idle: 59.8972% Other: 0.477385% Players online: 176
 Time (ms)     Calls     Rel usage %    Real usage % Description
      3200     32857       26.92365%       10.66862% std::bind(&Game::checkCreatureAttack, &g_game, getID())
 
I feel like it did nothing. I might be wrong; maybe it's a small improvement.

Before:
Code:
[11/05/2024 12:54:18]
Thread: 1 Cpu usage: 42.5874% Idle: 56.8241% Other: 0.588483% Players online: 181
 Time (ms)     Calls     Rel usage %    Real usage % Description
      3470     33494       27.16133%       11.56731% std::bind(&Game::checkCreatureAttack, &g_game, getID())

After:
Code:
[11/05/2024 16:20:48]
Thread: 1 Cpu usage: 39.6254% Idle: 59.8972% Other: 0.477385% Players online: 176
 Time (ms)     Calls     Rel usage %    Real usage % Description
      3200     32857       26.92365%       10.66862% std::bind(&Game::checkCreatureAttack, &g_game, getID())
It's not about improvement of normal attacks. It's fix for bug.
Follow "How to reproduce:" steps and your server CPU will go 100% after few minutes/hours.
 
It's not about improvement of normal attacks. It's fix for bug.
Oh thought it would fix how Game::checkCreatureAttack is always consuming like 50% of the CPU usage.
However, after a few months of hosting, I think these main functions have to simply drain the CPU as they're always being called. So there isn't much of a solution for them.
These are what I mean by main-used functions:
C++:
Game::checkCreatureAttack
LuaEnvironment::executeTimerEvent
Game::checkCreatures
Game::playerSay
scheduleSendAll
 
Back
Top