• 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++ Crash with Fir3elements 3777 (Log Provided)

Extrodus

|| Blazera.net ||
Premium User
Joined
Dec 22, 2008
Messages
2,737
Solutions
7
Reaction score
541
Location
Canada
Crash Information:
Server crashes when a player uses "/cast on" -> spectator 1 joins cast -> spectator 1 leaves (ctrl+l) -> server crashes.
Sometimes can happen just from using "/cast on" then "/cast off" as well.

Sources: GitHub - Fir3element/3777: The Forgotten Server 0.4 (rev 3777) with several improvements and bugfixes (https://github.com/Fir3element/3777)
OS: Ubuntu 20.04.3 LTS (Crash does NOT happen on Ubuntu 16.04.)

sudo apt-get update
sudo apt-get install --no-install-recommends -y git autoconf automake pkg-config build-essential cmake liblua5.1-0-dev libsqlite3-dev libmysqlclient-dev libxml2-dev libgmp3-dev libboost-filesystem-dev libboost-regex-dev libboost-thread-dev
git clone https://github.com/Fir3element/3777
cd /3777-master
mkdir build
cd build
cmake ..
make -j$(grep processor /proc/cpuinfo | wc -l)

Crash Log Information:
C++:
(gdb) bt full
#0  0x0000ffffacfeb24c in std::_Rb_tree_increment(std::_Rb_tree_node_base*) ()
   from /lib/aarch64-linux-gnu/libstdc++.so.6
No symbol table info available.
#1  0x0000aaaad6c04bd8 in ProtocolGame::logout(bool, bool) ()
No symbol table info available.
#2  0x0000aaaad6ad29c8 in Dispatcher::dispatcherThread(void*) ()
No symbol table info available.
#3  0x0000ffffad392624 in ?? () from /lib/aarch64-linux-gnu/libboost_thread.so.1.71.0
No symbol table info available.
#4  0x0000fffface39624 in start_thread (arg=0xffffad392598) at pthread_create.c:477
        ret = <optimized out>
        pd = <optimized out>
        unwind_buf = {cancel_jmp_buf = {{jmp_buf = {281473546450380, 281474911953024,
                281474911953022, 281473582485504, 281474911953023, 281473587946904,
                281473546451120, 281473546449328, 281473582489600, 281473546449328,
                281473546447248, 14304704568303608131, 0, 14304704568342650319, 0, 0, 0,
                0, 0, 0, 0, 0}, mask_was_saved = 0}}, priv = {pad = {0x0, 0x0, 0x0,
              0x0}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}}
        not_first_call = <optimized out>
#5  0x0000ffffacd9049c in thread_start ()
    at ../sysdeps/unix/sysv/linux/aarch64/clone.S:78
No locals.

C++:
bool ProtocolGame::logout(bool displayEffect, bool forceLogout)
{
    //dispatcher thread
    if(!player)
        return false;
        
    if(getIsCast() && !player->isAccountManager())
    {
        PlayerCast pc = player->getCast();
        for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
            if(it->second == this)
                if(Connection_ptr connection = it->second->getConnection())
                {
                    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
                    if(channel) {
                        channel->talk("", SPEAK_CHANNEL_RA, (getViewerName() + " has left the cast."));
                    }

                    connection->close();
                    player->removeCastViewer(it->first);
                }
        return false;
    }

    if(!player->isRemoved())
    {
        if(!forceLogout)
        {
            if(!IOLoginData::getInstance()->hasCustomFlag(player->getAccount(), PlayerCustomFlag_CanLogoutAnytime))
            {
                if(player->getTile()->hasFlag(TILESTATE_NOLOGOUT))
                {
                    player->sendCancelMessage(RET_YOUCANNOTLOGOUTHERE);
                    return false;
                }

                if(player->getZone() != ZONE_PROTECTION && player->hasCondition(CONDITION_INFIGHT))
                {
                    player->sendCancelMessage(RET_YOUMAYNOTLOGOUTDURINGAFIGHT);
                    return false;
                }

                if(!g_creatureEvents->playerLogout(player, false)) //let the script handle the error message
                    return false;
            }
            else
                g_creatureEvents->playerLogout(player, true);
        }
        else if(!g_creatureEvents->playerLogout(player, true))
            return false;

        if(displayEffect && !player->isGhost())
            g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF);
    }

    player->kickCastViewers();

    if(Connection_ptr connection = getConnection())
        connection->close();

    if(player->isRemoved())
        return true;

    return g_game.removeCreature(player);
}

@Fir3element - Any ideas what could be causing this?

Regards and as always, thank you for any help in advance!
 
Last edited:
Solution
Finally I found my old fix. Someone reported it few months ago.
I've made pull request with these changes:

protocolgame.cpp:
C++:
bool ProtocolGame::logout(bool displayEffect, bool forceLogout)
{
    //dispatcher thread
    if(!player)
        return false;
       
    if(getIsCast() && !player->isAccountManager())
    {
        PlayerCast pc = player->getCast();
        for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
            if(it->second == this)
                if(Connection_ptr connection = it->second->getConnection())
                {
                    PrivateChatChannel* channel =...
As I remember it's this:
C++:
        PlayerCast pc = player->getCast();
        for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
            if(it->second == this)
                if(Connection_ptr connection = it->second->getConnection())
                {
                    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
                    if(channel) {
                        channel->talk("", SPEAK_CHANNEL_RA, (getViewerName() + " has left the cast."));
                    }

                    connection->close();
                    player->removeCastViewer(it->first);
                }
        return false;
Iterating over list:
C++:
for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
and removing elements of list in same time:
C++:
player->removeCastViewer(it->first);
Code that removes cSpectators element is in function removeCastViewer.

There should be some code that reset it after every element removed.
Maybe something like:
C++:
player->removeCastViewer(it->first);
it = Player::cSpectators.begin(); // new line
It's not efficient solution, but to make it efficient, you would need to rewrite big part of 'cast system'.
 
As I remember it's this:
C++:
        PlayerCast pc = player->getCast();
        for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
            if(it->second == this)
                if(Connection_ptr connection = it->second->getConnection())
                {
                    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
                    if(channel) {
                        channel->talk("", SPEAK_CHANNEL_RA, (getViewerName() + " has left the cast."));
                    }

                    connection->close();
                    player->removeCastViewer(it->first);
                }
        return false;
Iterating over list:
C++:
for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
and removing elements of list in same time:
C++:
player->removeCastViewer(it->first);
Code that removes cSpectators element is in function removeCastViewer.

There should be some code that reset it after every element removed.
Maybe something like:
C++:
player->removeCastViewer(it->first);
it = Player::cSpectators.begin(); // new line
It's not efficient solution, but to make it efficient, you would need to rewrite big part of 'cast system'.
Thanks for the response; tested the code and server is still crashing.
You are definitely right about the issue as it seems that is how they fixed the crash with Slavi~


C++:
if(getIsCast() && !player->isAccountManager())
    {
        PlayerCast pc = player->getCast();
        for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
            if(it->second == this)
                if(Connection_ptr connection = it->second->getConnection())
                {
                    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
                    if(channel) {
                        channel->talk("", SPEAK_CHANNEL_RA, (getViewerName() + " has left the cast."));
                    }

                    connection->close();
                    player->removeCastViewer(it->first);
                    it = Player::cSpectators.begin(); // new line
                }
        return false;
    }
 
Thanks for the response; tested the code and server is still crashing.
You are definitely right about the issue as it seems that is how they fixed the crash with Slavi~
There is also other code with casts and removing element from list while iterating over it.
It's called in ProtocolGame::logout in line:
C++:
player->kickCastViewers();
It's defined in player.h:
C++:
void kickCastViewers()
{
   for(AutoList<ProtocolGame>::iterator it = cSpectators.begin(); it != cSpectators.end(); ++it)
   {
      if(it->second->getPlayer() == this)
      {
         it->second->disconnect();
         it->second->unRef();
         removeCastViewer(it->first);
      }
   }
   cast = PlayerCast();
}
So again, replace:
C++:
removeCastViewer(it->first);
with:
C++:
removeCastViewer(it->first);
it = cSpectators.begin();
 
There is also other code with casts and removing element from list while iterating over it.
It's called in ProtocolGame::logout in line:
C++:
player->kickCastViewers();
It's defined in player.h:
C++:
void kickCastViewers()
{
   for(AutoList<ProtocolGame>::iterator it = cSpectators.begin(); it != cSpectators.end(); ++it)
   {
      if(it->second->getPlayer() == this)
      {
         it->second->disconnect();
         it->second->unRef();
         removeCastViewer(it->first);
      }
   }
   cast = PlayerCast();
}
So again, replace:
C++:
removeCastViewer(it->first);
with:
C++:
removeCastViewer(it->first);
it = cSpectators.begin();
Unfortunately this still causes the server to crash when the spectator logs out; I really appreciate youre help though!

Core/BT Full
C++:
(gdb) thread apply all bt

Thread 3 (Thread 0xffffaf6371b0 (LWP 11243)):
#0  futex_abstimed_wait_cancelable (private=0, abstime=0xffffaf636818, clockid=<optimized out>, expected=0, futex_word=0xaaaacb1c927c <Scheduler::getInstance()::scheduler+204>) at ../sysdeps/nptl/futex-internal.h:320
#1  __pthread_cond_wait_common (abstime=0xffffaf636818, clockid=<optimized out>, mutex=0xaaaacb1c9220 <Scheduler::getInstance()::scheduler+112>, cond=0xaaaacb1c9250 <Scheduler::getInstance()::scheduler+160>) at pthread_cond_wait.c:520
#2  __pthread_cond_timedwait (cond=0xaaaacb1c9250 <Scheduler::getInstance()::scheduler+160>, mutex=0xaaaacb1c9220 <Scheduler::getInstance()::scheduler+112>, abstime=0xffffaf636818) at pthread_cond_wait.c:665
#3  0x0000aaaacb003ce8 in boost::condition_variable::do_wait_until(boost::unique_lock<boost::mutex>&, boost::detail::mono_platform_timepoint const&) ()
#4  0x0000aaaacb003c10 in boost::condition_variable::timed_wait(boost::unique_lock<boost::mutex>&, boost::posix_time::ptime const&) ()
#5  0x0000aaaacb002bf8 in Scheduler::schedulerThread(void*) ()
#6  0x0000aaaacae16350 in void boost::_bi::list1<boost::_bi::value<void*> >::operator()<void (*)(void*), boost::_bi::list0>(boost::_bi::type<void>, void (*&)(void*), boost::_bi::list0&, int) ()
#7  0x0000aaaacae1616c in boost::_bi::bind_t<void, void (*)(void*), boost::_bi::list1<boost::_bi::value<void*> > >::operator()() ()
#8  0x0000aaaacae15ec0 in boost::detail::thread_data<boost::_bi::bind_t<void, void (*)(void*), boost::_bi::list1<boost::_bi::value<void*> > > >::run() ()
#9  0x0000ffffb25ee624 in ?? () from /lib/aarch64-linux-gnu/libboost_thread.so.1.71.0
#10 0x0000ffffb2095624 in start_thread (arg=0xffffb25ee598) at pthread_create.c:477
#11 0x0000ffffb1fec49c in thread_start () at ../sysdeps/unix/sysv/linux/aarch64/clone.S:78

Thread 2 (Thread 0xffffafe5b1b0 (LWP 11242)):
#0  0x0000ffffb2247250 in std::_Rb_tree_increment(std::_Rb_tree_node_base*) () from /lib/aarch64-linux-gnu/libstdc++.so.6
#1  0x0000aaaacad7fb6c in std::_Rb_tree_iterator<std::pair<unsigned int const, ProtocolGame*> >::operator++() ()
#2  0x0000aaaacafc77a0 in ProtocolGame::logout(bool, bool) ()
#3  0x0000aaaacafec108 in boost::_mfi::mf2<bool, ProtocolGame, bool, bool>::operator()(ProtocolGame*, bool, bool) const ()
--Type <RET> for more, q to quit, c to continue without paging--
#4  0x0000aaaacafe8078 in bool boost::_bi::list3<boost::_bi::value<ProtocolGame*>, boost::_bi::value<bool>, boost::_bi::value<bool> >::operator()<bool, boost::_mfi::mf2<bool, ProtocolGame, bool, bool>, boost::_bi::list0>(boost::_bi::type<bool>, boost::_mfi::mf2<bool, ProtocolGame, bool, bool>&, boost::_bi::list0&, long) ()
#5  0x0000aaaacafe50d4 in boost::_bi::bind_t<bool, boost::_mfi::mf2<bool, ProtocolGame, bool, bool>, boost::_bi::list3<boost::_bi::value<ProtocolGame*>, boost::_bi::value<bool>, boost::_bi::value<bool> > >::operator()() ()
#6  0x0000aaaacafe35e8 in boost::detail::function::void_function_obj_invoker0<boost::_bi::bind_t<bool, boost::_mfi::mf2<bool, ProtocolGame, bool, bool>, boost::_bi::list3<boost::_bi::value<ProtocolGame*>, boost::_bi::value<bool>, boost::_bi::value<bool> > >, void>::invoke(boost::detail::function::function_buffer&) ()
#7  0x0000aaaacae1313c in boost::function0<void>::operator()() const ()
#8  0x0000aaaacae127e4 in Task::operator()() ()
#9  0x0000aaaacae111fc in Dispatcher::dispatcherThread(void*) ()
#10 0x0000aaaacae16350 in void boost::_bi::list1<boost::_bi::value<void*> >::operator()<void (*)(void*), boost::_bi::list0>(boost::_bi::type<void>, void (*&)(void*), boost::_bi::list0&, int) ()
#11 0x0000aaaacae1616c in boost::_bi::bind_t<void, void (*)(void*), boost::_bi::list1<boost::_bi::value<void*> > >::operator()() ()
#12 0x0000aaaacae15ec0 in boost::detail::thread_data<boost::_bi::bind_t<void, void (*)(void*), boost::_bi::list1<boost::_bi::value<void*> > > >::run() ()
#13 0x0000ffffb25ee624 in ?? () from /lib/aarch64-linux-gnu/libboost_thread.so.1.71.0
#14 0x0000ffffb2095624 in start_thread (arg=0xffffb25ee598) at pthread_create.c:477
#15 0x0000ffffb1fec49c in thread_start () at ../sysdeps/unix/sysv/linux/aarch64/clone.S:78

Thread 1 (Thread 0xffffafe61440 (LWP 11241)):
#0  0x0000ffffb1fec5c4 in __GI_epoll_pwait (epfd=<optimized out>, events=0xffffe67bd638, maxevents=128, timeout=-1, set=0x0) at ../sysdeps/unix/sysv/linux/epoll_pwait.c:42
#1  0x0000aaaacadb1468 in boost::asio::detail::epoll_reactor::run(long, boost::asio::detail::op_queue<boost::asio::detail::scheduler_operation>&) ()
#2  0x0000aaaacadb2a64 in boost::asio::detail::scheduler::do_run_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&, boost::asio::detail::scheduler_thread_info&, boost::system::error_code const&) ()
--Type <RET> for more, q to quit, c to continue without paging--
#3  0x0000aaaacadb2510 in boost::asio::detail::scheduler::run(boost::system::error_code&) ()
#4  0x0000aaaacb00c448 in boost::asio::io_context::run() ()
#5  0x0000aaaacb00b3cc in ServiceManager::run() ()
#6  0x0000aaaacaf7731c in main ()
 
Last edited:
Finally I found my old fix. Someone reported it few months ago.
I've made pull request with these changes:

protocolgame.cpp:
C++:
bool ProtocolGame::logout(bool displayEffect, bool forceLogout)
{
    //dispatcher thread
    if(!player)
        return false;
       
    if(getIsCast() && !player->isAccountManager())
    {
        PlayerCast pc = player->getCast();
        for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
            if(it->second == this)
                if(Connection_ptr connection = it->second->getConnection())
                {
                    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
                    if(channel) {
                        channel->talk("", SPEAK_CHANNEL_RA, (getViewerName() + " has left the cast."));
                    }

                    connection->close();
                    player->removeCastViewer(it->first);
                    // we are kicking 1 cast viewer, there can't be more than 1 on list
                    // break loop after removing 1, so it won't iterate over modified list
                    return false;
                }
        return false;
    }

    if(!player->isRemoved())
    {
        if(!forceLogout)
        {
            if(!IOLoginData::getInstance()->hasCustomFlag(player->getAccount(), PlayerCustomFlag_CanLogoutAnytime))
            {
                if(player->getTile()->hasFlag(TILESTATE_NOLOGOUT))
                {
                    player->sendCancelMessage(RET_YOUCANNOTLOGOUTHERE);
                    return false;
                }

                if(player->getZone() != ZONE_PROTECTION && player->hasCondition(CONDITION_INFIGHT))
                {
                    player->sendCancelMessage(RET_YOUMAYNOTLOGOUTDURINGAFIGHT);
                    return false;
                }

                if(!g_creatureEvents->playerLogout(player, false)) //let the script handle the error message
                    return false;
            }
            else
                g_creatureEvents->playerLogout(player, true);
        }
        else if(!g_creatureEvents->playerLogout(player, true))
            return false;

        if(displayEffect && !player->isGhost())
            g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF);
    }

    player->kickCastViewers();

    if(Connection_ptr connection = getConnection())
        connection->close();

    if(player->isRemoved())
        return true;

    return g_game.removeCreature(player);
}
player.h
C++:
        void kickCastViewers()
        {
            // use proper 'remove while iterating over list' code:
            // it = list.erase(it);
            AutoList<ProtocolGame>::iterator it = cSpectators.begin();
            while (it != cSpectators.end()) {
                if (it->second->getPlayer() == this) {
                    it->second->disconnect();
                    it->second->unRef();
                    it = cSpectators.erase(it);
                } else {
                    ++it;
                }
            }
            cast = PlayerCast();
        }

        void kickCastViewerByName(std::string n)
        {
            for(AutoList<ProtocolGame>::iterator it = cSpectators.begin(); it != cSpectators.end(); ++it) if(it->second->getPlayer() == this)
                if(it->second->getViewerName() == n && it->second->getPlayer() == this)
                {
                    it->second->disconnect();
                    it->second->unRef();
                    removeCastViewer(it->first);
                    // there should be 1 cast viewer with given name
                    // break loop after removing 1, so it won't iterate over modified list
                    return;
                }
        }
 
Last edited:
Solution
Finally I found my old fix. Someone reported it few months ago.
I've made pull request with these changes:

protocolgame.cpp:
C++:
bool ProtocolGame::logout(bool displayEffect, bool forceLogout)
{
    //dispatcher thread
    if(!player)
        return false;
      
    if(getIsCast() && !player->isAccountManager())
    {
        PlayerCast pc = player->getCast();
        for(AutoList<ProtocolGame>::iterator it = Player::cSpectators.begin(); it != Player::cSpectators.end(); ++it)
            if(it->second == this)
                if(Connection_ptr connection = it->second->getConnection())
                {
                    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
                    if(channel) {
                        channel->talk("", SPEAK_CHANNEL_RA, (getViewerName() + " has left the cast."));
                    }

                    connection->close();
                    player->removeCastViewer(it->first);
                    // we are kicking 1 cast viewer, there can't be more than 1 on list
                    // break loop after removing 1, so it won't iterate over modified list
                    return false;
                }
        return false;
    }

    if(!player->isRemoved())
    {
        if(!forceLogout)
        {
            if(!IOLoginData::getInstance()->hasCustomFlag(player->getAccount(), PlayerCustomFlag_CanLogoutAnytime))
            {
                if(player->getTile()->hasFlag(TILESTATE_NOLOGOUT))
                {
                    player->sendCancelMessage(RET_YOUCANNOTLOGOUTHERE);
                    return false;
                }

                if(player->getZone() != ZONE_PROTECTION && player->hasCondition(CONDITION_INFIGHT))
                {
                    player->sendCancelMessage(RET_YOUMAYNOTLOGOUTDURINGAFIGHT);
                    return false;
                }

                if(!g_creatureEvents->playerLogout(player, false)) //let the script handle the error message
                    return false;
            }
            else
                g_creatureEvents->playerLogout(player, true);
        }
        else if(!g_creatureEvents->playerLogout(player, true))
            return false;

        if(displayEffect && !player->isGhost())
            g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF);
    }

    player->kickCastViewers();

    if(Connection_ptr connection = getConnection())
        connection->close();

    if(player->isRemoved())
        return true;

    return g_game.removeCreature(player);
}
player.h
C++:
        void kickCastViewers()
        {
            // use proper 'remove while iterating over list' code:
            // it = list.erase(it);
            AutoList<ProtocolGame>::iterator it = cSpectators.begin();
            while (it != cSpectators.end()) {
                if (it->second->getPlayer() == this) {
                    it->second->disconnect();
                    it->second->unRef();
                    it = cSpectators.erase(it);
                } else {
                    ++it;
                }
            }
            cast = PlayerCast();
        }

        void kickCastViewerByName(std::string n)
        {
            for(AutoList<ProtocolGame>::iterator it = cSpectators.begin(); it != cSpectators.end(); ++it) if(it->second->getPlayer() == this)
                if(it->second->getViewerName() == n && it->second->getPlayer() == this)
                {
                    it->second->disconnect();
                    it->second->unRef();
                    removeCastViewer(it->first);
                    // there should be 1 cast viewer with given name
                    // break loop after removing 1, so it won't iterate over modified list
                    return;
                }
        }

Windows devcpp
Error:
C++:
actions.cpp In member function 'void Player::kickCastViewers()':
237 player.h no match for 'operator=' in 'it = Player::cSpectators.AutoList<ProtocolGame>::<anonymous>.std::map<_Key, _Tp, _Compare, _Alloc>::erase [with _Key = unsigned int, _Tp = ProtocolGame*, _Compare = std::less<unsigned int>, _Alloc = std::allocator<std::pair<const unsigned int, ProtocolGame*> >](it)'
candidates are: std::_Rb_tree_iterator<std::pair<const unsigned int, ProtocolGame*> >& std::_Rb_tree_iterator<std::pair<const unsigned int, ProtocolGame*> >::operator=(const std::_Rb_tree_iterator<std::pair<const unsigned int, ProtocolGame*> >&)
 
Windows devcpp
Error:
C++:
actions.cpp In member function 'void Player::kickCastViewers()':
237 player.h no match for 'operator=' in 'it = Player::cSpectators.AutoList<ProtocolGame>::<anonymous>.std::map<_Key, _Tp, _Compare, _Alloc>::erase [with _Key = unsigned int, _Tp = ProtocolGame*, _Compare = std::less<unsigned int>, _Alloc = std::allocator<std::pair<const unsigned int, ProtocolGame*> >](it)'
candidates are: std::_Rb_tree_iterator<std::pair<const unsigned int, ProtocolGame*> >& std::_Rb_tree_iterator<std::pair<const unsigned int, ProtocolGame*> >::operator=(const std::_Rb_tree_iterator<std::pair<const unsigned int, ProtocolGame*> >&)
Probably DevCpp lives in 2010 year and it does not support it = cSpectators.erase(it); :(
(map.erase function does not return anything, since C++11 (2011) it does return iterator)

You can try to replace:
Code:
it = cSpectators.erase(it);
with:
Code:
cSpectators.erase(it);
it = cSpectators.begin();
 
Back
Top