• 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!

C++ Slavidodo casting-system crash on logout

Addams

OTMadness.com OTSes Services
Staff member
Board Moderator
Joined
Sep 29, 2009
Messages
2,920
Solutions
342
Reaction score
1,688
Location
Egypt
I've noticed that server randomly crashes on caster/spectator logout if both of them logged out at the same time or if 2 casters logged out at the same time.
I did read a solution on Github on 1 of the replies saying:
I have probably fixed it by copying kick functions from ProtocolGame::release() to logout(). I observe that release() function is not casted instantly on logout, but on next ProtocolGame task, such as login...
So I followed his solution and moved them from release() to logout().
release() before my changes.
C++:
void ProtocolGame::release()
{
    //dispatcher thread
    if (player && player->client == shared_from_this()) {

        // in the dispatcher thread, this should release the protocol after the player is null and this protocol has released
        // so we must exclude every spectator before releasing
        if (isLiveCasting()) {
            sendChannelEvent(CHANNEL_CAST, player->name, CHANNELEVENT_EXCLUDE);
            for (auto spectator : castinfo.spectators) {
                sendChannelEvent(CHANNEL_CAST, spectator->name, CHANNELEVENT_EXCLUDE); // which is gonna be sent to every spectator
            }

            stopLiveCasting();
        }

        player->client.reset();
        player->decrementReferenceCounter();
        player = nullptr;
    }

    OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
    Protocol::release();
}
release() after my changes.
C++:
void ProtocolGame::release()
{
    //dispatcher thread
    if (player && player->client == shared_from_this()) {
        player->client.reset();
        player->decrementReferenceCounter();
        player = nullptr;
    }

    OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
    Protocol::release();
}
logout() before my changes.
C++:
void ProtocolGame::logout(bool displayEffect, bool forced)
{
    //dispatcher thread
    if (!player) {
        return;
    }

    if (!player->isRemoved()) {
        if (!forced) {
            if (!player->isAccessPlayer()) {
                if (player->getTile()->hasFlag(TILESTATE_NOLOGOUT)) {
                    player->sendCancelMessage(RETURNVALUE_YOUCANNOTLOGOUTHERE);
                    return;
                }

                if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT)) {
                    player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT);
                    return;
                }
            }

            //scripting event - onLogout
            if (!g_creatureEvents->playerLogout(player)) {
                //Let the script handle the error message
                return;
            }
        }

        if (displayEffect && player->getHealth() > 0) {
            g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
        }
    }

    disconnect();

    g_game.removeCreature(player);
}
logout() after my changes.
C++:
void ProtocolGame::logout(bool displayEffect, bool forced)
{
    //dispatcher thread
    if (!player) {
        return;
    }

    if (!player->isRemoved()) {
        if (!forced) {
            if (!player->isAccessPlayer()) {
                if (player->getTile()->hasFlag(TILESTATE_NOLOGOUT)) {
                    player->sendCancelMessage(RETURNVALUE_YOUCANNOTLOGOUTHERE);
                    return;
                }

                if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT)) {
                    player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT);
                    return;
                }
            }

            //scripting event - onLogout
            if (!g_creatureEvents->playerLogout(player)) {
                //Let the script handle the error message
                return;
            }
        }

        if (displayEffect && player->getHealth() > 0 && !player->isInGhostMode()) {
            g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
            }
        }
        //dispatcher thread
        if (player && player->client == shared_from_this()) {
        // in the dispatcher thread, this should release the protocol after the player is null and this protocol has released
        // so we must exclude every spectator before releasing
        if (isLiveCasting()) {
            sendChannelEvent(CHANNEL_CAST, player->name, CHANNELEVENT_EXCLUDE);
            for (auto spectator : castinfo.spectators) {
                sendChannelEvent(CHANNEL_CAST, spectator->name, CHANNELEVENT_EXCLUDE); // which is gonna be sent to every spectator
            }
        }
            stopLiveCasting();
        }
 
    disconnect();
    g_game.removeCreature(player);
}
When server crashes it points me to this line in protocolspectator.cpp.
C++:
OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
Here's the function containing this line.
C++:
void ProtocolSpectator::release()
{
    // dispatcher thread
    if (player && casterProtocol && player->client == casterProtocol->shared_from_this()) {
        casterProtocol->castinfo.spectators.erase(
            std::find(casterProtocol->castinfo.spectators.begin(),
                casterProtocol->castinfo.spectators.end(), getThis())
        );

        casterProtocol->sendChannelEvent(CHANNEL_CAST, name, CHANNELEVENT_EXCLUDE);

        TextMessage message = TextMessage(MESSAGE_STATUS_WARNING, name + " has left the channel.");
        message.channelId = CHANNEL_CAST;
        casterProtocol->sendTextMessage(message);

        IOLoginData::updateCast(player->getGUID(), casterProtocol->castinfo.spectators.size());
    }

    OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
}
I know that's not the normal type of Support threads people post and it's harder than what people expect but it's the only free system out there that I found and I can add and also I've seen several other people who successfully added it so sharing their own solutions wouldn't harm I believe.
Here's a link to the system too.
Edit:
There's also a copy of Visual Studio debugger messages.
Autos:
Code:
+        casterProtocol    0x0000025c9c102cb0 {castinfo={lastSpectatorNum=0 enabled=false paused=false ...} }    ProtocolGame *
+        casterProtocol->castinfo    {lastSpectatorNum=0 enabled=false paused=false ...}    ProtocolGame::<unnamed-type-castinfo>
+        casterProtocol->castinfo.spectators    { size=0 }    std::vector<std::shared_ptr<ProtocolSpectator>,std::allocator<std::shared_ptr<ProtocolSpectator>>>
+        player    0x0000025c9c219020 {attackedSet={ size=0 } VIPList={ size=0 } openContainers={ size=0 } ...}    Player *
+        this    0x0000025cb77ec150 {lastSpeak=0 name="Spectator [1]" casterProtocol=0x0000025c9c102cb0 {castinfo={lastSpectatorNum=...} } }    ProtocolSpectator *
Locals:
Code:
+        this    0x0000025cb77ec150 {lastSpeak=0 name="Spectator [1]" casterProtocol=0x0000025c9c102cb0 {castinfo={lastSpectatorNum=...} } }    ProtocolSpectator *
+        message    {type=0 '\0' text="H‰\\$\x10H‰t$\x18H‰|$ UATAUAVAWHl$ÉHì " position={x=1 y=0 z=0 '\0' } ...}    TextMessage
Call Stack:
Code:
     theforgottenserver-x64.exe!std::_Throw_bad_weak_ptr() Line 1045    C++
     [Inline Frame] theforgottenserver-x64.exe!std::shared_ptr<Protocol>::{ctor}(const std::weak_ptr<Protocol> &) Line 1580    C++
     [Inline Frame] theforgottenserver-x64.exe!std::enable_shared_from_this<Protocol>::shared_from_this() Line 3080    C++
>    theforgottenserver-x64.exe!ProtocolSpectator::release() Line 198    C++
     [Inline Frame] theforgottenserver-x64.exe!std::_Func_class<void>::operator()() Line 880    C++
     [Inline Frame] theforgottenserver-x64.exe!Task::operator()() Line 25    C++
     theforgottenserver-x64.exe!Dispatcher::threadMain() Line 43    C++
     [External Code]
 
Last edited:
Solution
I believe I've solved it by combining the last 2 guesses up together, Kept my scripts debugging for 31 hours with 4 MCs and no crashes.
I am not sure if that's the correct solution but I am closing this anyway as I found 2 other different bugs so I won't bother adding this system for now.
  • Tried adding a check before removeProtocolFromAutosend like this if (player || casterProtocol || casterProtocol->isLiveCasting()) in ProtocolSpectator::release() still crashed.
  • Tried adding logout() functions before release() in protocolspectator.cpp like this
    C++:
    void ProtocolSpectator::logout(){
    disconnect();
    release();
    }
    and still crashed.
Those are the ones I added combined (which stopped the...
I am not good at reading VS error log but I thought it was trying to send the kick message even after the caster disconnected so I tried removing this part from the void ProtocolSpectator::release() and it still crashed.
C++:
TextMessage message = TextMessage(MESSAGE_STATUS_WARNING, name + " has left the channel.");
        message.channelId = CHANNEL_CAST;
        casterProtocol->sendTextMessage(message);
I just wanted to mention that crashes don't always occur, I am not sure if it's me not perfecting logout at the exact same time or if it's just random, It occurred once after 3 minutes, 10 minutes, 48 minutes, and last time it took like 66 minutes to occur.
 
I must say that I've never seen any cast system that worked.
1. TFS 0.4 'Elf system' - some memory leak that can be abused, not so hard to fix, after that it just worked (it works on Kasteria.pl's "otserv 0.6.3")
2. Djarek 1.x system - Tibia client debugs when there are many moving creatures on screen - problems with synchronization between game/spectators.
3. Now you say that Slavidodo crash server?

It's getting worse with every edition and somehow it's still not implemented in official TFS.
 
1. TFS 0.4 'Elf system' - some memory leak that can be abused, not so hard to fix, after that it just worked (it works on Kasteria.pl's "otserv 0.6.3")
I've seen this one working on different old servers using 0.4 or maybe they used a different private cast system, Not sure about the memory leaks tho but as a player it was good enough.
2. Djarek 1.x system - Tibia client debugs when there are many moving creatures on screen - problems with synchronization between game/spectators.
This one actually has the same crash issue I believe (Crashed once on logout but I wasn't debugging) and the same issue when sending a message if the player is using OTC because sendChannelMessage function needs to be changed from int16_t to int32_t.
I tested it for a few days before moving to Slavi's.
3. Now you say that Slavidodo crash server?
This one is supposed to be the most recent free/public one out there so I decided to give it a try too, I've solved a few issues with the help of Alpha and Nekiro but still this crash bug I can't solve by myself and also 1 not so important issue (for now) when using "!mute spectatorName" it just bans the spectator instead of muting him.
It's getting worse with every edition and somehow it's still not implemented in official TFS.
I've actually got many messages asking to help to add cast system in the past (which I never tried till now) and I know a few people who want to add it still but have the same issues too.
I don't know if there's any intention to add it to official TFS tho but no one bothered trying after Slavi's version yet.
It's a good/very easier alternative to Discord/Twitch for people with low PC specs or low internet speed and who want to take a quick view of the server, Just wanted to add this because I read somewhere some comments like just use Discord, etc...

@Thread: Do you know a solution or a rough guess?
 
Last edited:
Hi I also want to add this system to my server if I finished it can you help me add it I can pay you if you want Who can help me please send a private message
I hate new accounts, I can now imagine how many people would just assume you're me and that's a BUMP.
Just create a job thread, Someone offered to sell me the system for 130$ and I also went to test it on his 10.98 server. I can redirect you to him if you want, Just PM me.
As for me, I am here (in this section) to ask for free Support if I am willing to pay then I would have used Jobs section from the beginning.
 
Last edited:
I will post what I've tried till now and then continue my tests.
  • Tried adding stopLiveCasting(); function before the dispatcher thread in ProtocolGame::release() still crashed.
  • Tried adding acceptPackets = false; before the dispatcher thread in ProtocolSpectator::release() still crashed.
  • Tried adding casterProtocol->stopLiveCasting(); before the dispatcher thread in ProtocolSpectator::release() still crashed.
  • Tried adding a check before removeProtocolFromAutosend like this if (player || casterProtocol || casterProtocol->isLiveCasting()) in ProtocolSpectator::release() still crashed.
  • Tried adding logout() functions before release() in protocolspectator.cpp like this
    C++:
    void ProtocolSpectator::logout(){
        disconnect();
        release();
    }
    and still crashed.
Most of those are just rough guesses and checks as I can't actually understand the crash error log but all I know is that it's from protocolspectator.cpp and exactly this ProtocolSpectator::release() function as mentioned above in the crash explanation and how it's reproduced.
 
Last edited:
I believe I've solved it by combining the last 2 guesses up together, Kept my scripts debugging for 31 hours with 4 MCs and no crashes.
I am not sure if that's the correct solution but I am closing this anyway as I found 2 other different bugs so I won't bother adding this system for now.
  • Tried adding a check before removeProtocolFromAutosend like this if (player || casterProtocol || casterProtocol->isLiveCasting()) in ProtocolSpectator::release() still crashed.
  • Tried adding logout() functions before release() in protocolspectator.cpp like this
    C++:
    void ProtocolSpectator::logout(){
    disconnect();
    release();
    }
    and still crashed.
Those are the ones I added combined (which stopped the crash) in case someone wants to continue where I've stopped.
 
Solution
I believe I've solved it by combining the last 2 guesses up together, Kept my scripts debugging for 31 hours with 4 MCs and no crashes.
I am not sure if that's the correct solution but I am closing this anyway as I found 2 other different bugs so I won't bother adding this system for now.

Those are the ones I added combined (which stopped the crash) in case someone wants to continue where I've stopped.
contribute to the community this cast system
 
Back
Top