Canceling asio timers has bad semantics, and causes loads of trouble. Therefore, cancelled timers are simply marked as such, and the asio callback is silently ignored. This fixes several crashes, if players were removed from a game due to an error, because some timers were not cancelled. *sigh* This one took a lot of nerve.
This commit is contained in:
@@ -35,31 +35,30 @@ TimerManager::RegisterTimer(unsigned timeoutMsec, boost::function<void()> timerH
|
|||||||
boost::recursive_mutex::scoped_lock lock(m_timerMutex);
|
boost::recursive_mutex::scoped_lock lock(m_timerMutex);
|
||||||
// Use a unique id for each timer.
|
// Use a unique id for each timer.
|
||||||
unsigned id = GetNextTimerId();
|
unsigned id = GetNextTimerId();
|
||||||
TimerData data;
|
boost::shared_ptr<TimerData> data(new TimerData);
|
||||||
data.timer.reset(
|
data->timer.reset(
|
||||||
new boost::asio::deadline_timer(*m_ioService, boost::posix_time::milliseconds(timeoutMsec)));
|
new boost::asio::deadline_timer(*m_ioService, boost::posix_time::milliseconds(timeoutMsec)));
|
||||||
data.userHandler = timerHandler;
|
data->userHandler = timerHandler;
|
||||||
data.durationMsec = timeoutMsec;
|
data->durationMsec = timeoutMsec;
|
||||||
data.autoRestart = autoRestart;
|
data->autoRestart = autoRestart;
|
||||||
data.timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, data));
|
data->cancelled = false;
|
||||||
|
data->timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, data));
|
||||||
m_timerMap.insert(TimerMap::value_type(id, data));
|
m_timerMap.insert(TimerMap::value_type(id, data));
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TimerManager::RestartTimer(unsigned timerId, unsigned timeoutMsec, boost::function<void()> timerHandler)
|
TimerManager::AddTimer(unsigned timerId, unsigned timeoutMsec, boost::function<void()> timerHandler)
|
||||||
{
|
{
|
||||||
boost::recursive_mutex::scoped_lock lock(m_timerMutex);
|
boost::recursive_mutex::scoped_lock lock(m_timerMutex);
|
||||||
bool restarted = false;
|
bool restarted = false;
|
||||||
TimerMap::iterator pos = m_timerMap.find(timerId);
|
TimerMap::iterator pos = m_timerMap.find(timerId);
|
||||||
if (pos != m_timerMap.end())
|
if (pos != m_timerMap.end())
|
||||||
{
|
{
|
||||||
pos->second.userHandler = timerHandler;
|
pos->second->userHandler = timerHandler;
|
||||||
boost::shared_ptr<boost::asio::deadline_timer> timer(pos->second.timer);
|
pos->second->timer->expires_from_now(boost::posix_time::milliseconds(timeoutMsec));
|
||||||
timer->cancel();
|
pos->second->timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, pos->second));
|
||||||
timer->expires_from_now(boost::posix_time::milliseconds(timeoutMsec));
|
|
||||||
timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, pos->second));
|
|
||||||
restarted = true;
|
restarted = true;
|
||||||
}
|
}
|
||||||
return restarted;
|
return restarted;
|
||||||
@@ -74,7 +73,7 @@ TimerManager::UnregisterTimer(unsigned timerId)
|
|||||||
TimerMap::iterator pos = m_timerMap.find(timerId);
|
TimerMap::iterator pos = m_timerMap.find(timerId);
|
||||||
if (pos != m_timerMap.end())
|
if (pos != m_timerMap.end())
|
||||||
{
|
{
|
||||||
pos->second.timer->cancel();
|
pos->second->cancelled = true;
|
||||||
m_timerMap.erase(pos);
|
m_timerMap.erase(pos);
|
||||||
unregistered = true;
|
unregistered = true;
|
||||||
}
|
}
|
||||||
@@ -82,15 +81,15 @@ TimerManager::UnregisterTimer(unsigned timerId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimerManager::Handler(boost::system::error_code ec, TimerManager::TimerData data)
|
TimerManager::Handler(const boost::system::error_code &ec, boost::shared_ptr<TimerManager::TimerData> data)
|
||||||
{
|
{
|
||||||
if (!ec)
|
if (!ec && data && !data->cancelled)
|
||||||
{
|
{
|
||||||
data.userHandler();
|
data->userHandler();
|
||||||
if (data.autoRestart)
|
if (data->autoRestart)
|
||||||
{
|
{
|
||||||
data.timer->expires_from_now(boost::posix_time::milliseconds(data.durationMsec));
|
data->timer->expires_from_now(boost::posix_time::milliseconds(data->durationMsec));
|
||||||
data.timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, data));
|
data->timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public:
|
|||||||
TimerManager(boost::shared_ptr<boost::asio::io_service> ioService);
|
TimerManager(boost::shared_ptr<boost::asio::io_service> ioService);
|
||||||
|
|
||||||
unsigned RegisterTimer(unsigned timeoutMsec, boost::function<void()> timerHandler, bool autoRestart = false);
|
unsigned RegisterTimer(unsigned timeoutMsec, boost::function<void()> timerHandler, bool autoRestart = false);
|
||||||
bool RestartTimer(unsigned timerId, unsigned timeoutMsec, boost::function<void()> timerHandler);
|
bool AddTimer(unsigned timerId, unsigned timeoutMsec, boost::function<void()> timerHandler);
|
||||||
bool UnregisterTimer(unsigned timerId);
|
bool UnregisterTimer(unsigned timerId);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -43,10 +43,11 @@ protected:
|
|||||||
boost::function<void()> userHandler;
|
boost::function<void()> userHandler;
|
||||||
unsigned durationMsec;
|
unsigned durationMsec;
|
||||||
bool autoRestart;
|
bool autoRestart;
|
||||||
|
bool cancelled;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void Handler(boost::system::error_code ec, TimerData data);
|
static void Handler(const boost::system::error_code &ec, boost::shared_ptr<TimerData> data);
|
||||||
typedef std::map<unsigned, TimerData> TimerMap;
|
typedef std::map<unsigned, boost::shared_ptr<TimerData> > TimerMap;
|
||||||
|
|
||||||
unsigned GetNextTimerId();
|
unsigned GetNextTimerId();
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class SendDataManager : public boost::enable_shared_from_this<SendDataManager>
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleWrite(const boost::system::error_code& error);
|
void HandleWrite(const boost::system::error_code &error);
|
||||||
|
|
||||||
void AsyncSendNextPacket(bool handlerMode = false);
|
void AsyncSendNextPacket(bool handlerMode = false);
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ class SendDataManager : public boost::enable_shared_from_this<SendDataManager>
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
SendDataManager::HandleWrite(const boost::system::error_code& error)
|
SendDataManager::HandleWrite(const boost::system::error_code &error)
|
||||||
{
|
{
|
||||||
// TODO error handling
|
// TODO error handling
|
||||||
AsyncSendNextPacket(true);
|
AsyncSendNextPacket(true);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#include <core/loghelper.h>
|
#include <core/loghelper.h>
|
||||||
|
|
||||||
|
|
||||||
#define NET_SERVER_LISTEN_BACKLOG 5
|
#define NET_SERVER_LISTEN_BACKLOG 20
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using boost::asio::ip::tcp;
|
using boost::asio::ip::tcp;
|
||||||
@@ -96,7 +96,7 @@ ServerAcceptHelper::InternalListen(unsigned serverPort, bool ipv6, bool sctp)
|
|||||||
|
|
||||||
void
|
void
|
||||||
ServerAcceptHelper::HandleAccept(boost::shared_ptr<boost::asio::ip::tcp::socket> acceptedSocket,
|
ServerAcceptHelper::HandleAccept(boost::shared_ptr<boost::asio::ip::tcp::socket> acceptedSocket,
|
||||||
const boost::system::error_code& error)
|
const boost::system::error_code &error)
|
||||||
{
|
{
|
||||||
if (!error)
|
if (!error)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,16 @@ ServerGame::ServerGame(ServerLobbyThread &lobbyThread, u_int32_t id, const strin
|
|||||||
LOG_VERBOSE("Game object " << GetId() << " created.");
|
LOG_VERBOSE("Game object " << GetId() << " created.");
|
||||||
|
|
||||||
m_receiver.reset(new ReceiverHelper);
|
m_receiver.reset(new ReceiverHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerGame::~ServerGame()
|
||||||
|
{
|
||||||
|
LOG_VERBOSE("Game object " << GetId() << " destructed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ServerGame::Init()
|
||||||
|
{
|
||||||
m_voteKickTimerId = GetLobbyThread().GetTimerManager().RegisterTimer(
|
m_voteKickTimerId = GetLobbyThread().GetTimerManager().RegisterTimer(
|
||||||
SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC,
|
SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC,
|
||||||
boost::bind(&ServerGame::TimerVoteKick, this),
|
boost::bind(&ServerGame::TimerVoteKick, this),
|
||||||
@@ -55,12 +64,11 @@ ServerGame::ServerGame(ServerLobbyThread &lobbyThread, u_int32_t id, const strin
|
|||||||
SetState(SERVER_INITIAL_STATE::Instance());
|
SetState(SERVER_INITIAL_STATE::Instance());
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerGame::~ServerGame()
|
void
|
||||||
|
ServerGame::Exit()
|
||||||
{
|
{
|
||||||
GetLobbyThread().GetTimerManager().UnregisterTimer(m_voteKickTimerId);
|
GetLobbyThread().GetTimerManager().UnregisterTimer(m_voteKickTimerId);
|
||||||
GetLobbyThread().GetTimerManager().UnregisterTimer(m_stateTimerId);
|
GetLobbyThread().GetTimerManager().UnregisterTimer(m_stateTimerId);
|
||||||
|
|
||||||
LOG_VERBOSE("Game object " << GetId() << " destructed.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u_int32_t
|
u_int32_t
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ ServerGameStateInit::TimerAdminWarning(ServerGame &server)
|
|||||||
server.GetLobbyThread().GetSender().Send(session.sessionData, warning);
|
server.GetLobbyThread().GetSender().Send(session.sessionData, warning);
|
||||||
}
|
}
|
||||||
// Start timeout timer.
|
// Start timeout timer.
|
||||||
server.GetLobbyThread().GetTimerManager().RestartTimer(
|
server.GetLobbyThread().GetTimerManager().AddTimer(
|
||||||
server.GetStateTimerId(),
|
server.GetStateTimerId(),
|
||||||
SERVER_GAME_ADMIN_WARNING_REMAINING_SEC * 1000,
|
SERVER_GAME_ADMIN_WARNING_REMAINING_SEC * 1000,
|
||||||
boost::bind(&ServerGameStateInit::TimerAdminTimeout, this, boost::ref(server)));
|
boost::bind(&ServerGameStateInit::TimerAdminTimeout, this, boost::ref(server)));
|
||||||
@@ -701,7 +701,7 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
|
|||||||
server.SendToAllPlayers(allIn, SessionData::Game);
|
server.SendToAllPlayers(allIn, SessionData::Game);
|
||||||
curGame.getCurrentHand()->setCardsShown(true);
|
curGame.getCurrentHand()->setCardsShown(true);
|
||||||
|
|
||||||
server.GetLobbyThread().GetTimerManager().RestartTimer(
|
server.GetLobbyThread().GetTimerManager().AddTimer(
|
||||||
server.GetStateTimerId(),
|
server.GetStateTimerId(),
|
||||||
SERVER_SHOW_CARDS_DELAY_SEC * 1000,
|
SERVER_SHOW_CARDS_DELAY_SEC * 1000,
|
||||||
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
|
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
|
||||||
@@ -710,7 +710,7 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
|
|||||||
{
|
{
|
||||||
SendNewRoundCards(server, curGame, newRound);
|
SendNewRoundCards(server, curGame, newRound);
|
||||||
|
|
||||||
server.GetLobbyThread().GetTimerManager().RestartTimer(
|
server.GetLobbyThread().GetTimerManager().AddTimer(
|
||||||
server.GetStateTimerId(),
|
server.GetStateTimerId(),
|
||||||
GetDealCardsDelaySec(server) * 1000,
|
GetDealCardsDelaySec(server) * 1000,
|
||||||
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
|
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
|
||||||
@@ -741,7 +741,7 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
|
|||||||
// If the player is computer controlled, let the engine act.
|
// If the player is computer controlled, let the engine act.
|
||||||
if (curPlayer->getMyType() == PLAYER_TYPE_COMPUTER)
|
if (curPlayer->getMyType() == PLAYER_TYPE_COMPUTER)
|
||||||
{
|
{
|
||||||
server.GetLobbyThread().GetTimerManager().RestartTimer(
|
server.GetLobbyThread().GetTimerManager().AddTimer(
|
||||||
server.GetStateTimerId(),
|
server.GetStateTimerId(),
|
||||||
SERVER_COMPUTER_ACTION_DELAY_SEC * 1000,
|
SERVER_COMPUTER_ACTION_DELAY_SEC * 1000,
|
||||||
boost::bind(&ServerGameStateHand::TimerComputerAction, this, boost::ref(server)));
|
boost::bind(&ServerGameStateHand::TimerComputerAction, this, boost::ref(server)));
|
||||||
@@ -750,7 +750,7 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
|
|||||||
else if (!server.GetSessionManager().IsPlayerConnected(curPlayer->getMyName()))
|
else if (!server.GetSessionManager().IsPlayerConnected(curPlayer->getMyName()))
|
||||||
{
|
{
|
||||||
PerformPlayerAction(server, curPlayer, PLAYER_ACTION_FOLD, 0);
|
PerformPlayerAction(server, curPlayer, PLAYER_ACTION_FOLD, 0);
|
||||||
server.GetLobbyThread().GetTimerManager().RestartTimer(
|
server.GetLobbyThread().GetTimerManager().AddTimer(
|
||||||
server.GetStateTimerId(),
|
server.GetStateTimerId(),
|
||||||
SERVER_LOOP_DELAY_MSEC,
|
SERVER_LOOP_DELAY_MSEC,
|
||||||
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
|
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
|
||||||
@@ -833,14 +833,14 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
|
|||||||
else if (playersWithCash.size() == 1)
|
else if (playersWithCash.size() == 1)
|
||||||
{
|
{
|
||||||
// View a dialog for a new game - delayed.
|
// View a dialog for a new game - delayed.
|
||||||
server.GetLobbyThread().GetTimerManager().RestartTimer(
|
server.GetLobbyThread().GetTimerManager().AddTimer(
|
||||||
server.GetStateTimerId(),
|
server.GetStateTimerId(),
|
||||||
SERVER_DELAY_NEXT_GAME_SEC * 1000,
|
SERVER_DELAY_NEXT_GAME_SEC * 1000,
|
||||||
boost::bind(&ServerGameStateHand::TimerNextGame, this, boost::ref(server)));
|
boost::bind(&ServerGameStateHand::TimerNextGame, this, boost::ref(server)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
server.GetLobbyThread().GetTimerManager().RestartTimer(
|
server.GetLobbyThread().GetTimerManager().AddTimer(
|
||||||
server.GetStateTimerId(),
|
server.GetStateTimerId(),
|
||||||
SERVER_DELAY_NEXT_HAND_SEC * 1000,
|
SERVER_DELAY_NEXT_HAND_SEC * 1000,
|
||||||
boost::bind(&ServerGameStateHand::TimerNextHand, this, boost::ref(server)));
|
boost::bind(&ServerGameStateHand::TimerNextHand, this, boost::ref(server)));
|
||||||
@@ -855,7 +855,7 @@ ServerGameStateHand::TimerShowCards(ServerGame &server)
|
|||||||
Game &curGame = server.GetGame();
|
Game &curGame = server.GetGame();
|
||||||
SendNewRoundCards(server, curGame, curGame.getCurrentHand()->getCurrentRound());
|
SendNewRoundCards(server, curGame, curGame.getCurrentHand()->getCurrentRound());
|
||||||
|
|
||||||
server.GetLobbyThread().GetTimerManager().RestartTimer(
|
server.GetLobbyThread().GetTimerManager().AddTimer(
|
||||||
server.GetStateTimerId(),
|
server.GetStateTimerId(),
|
||||||
GetDealCardsDelaySec(server) * 1000,
|
GetDealCardsDelaySec(server) * 1000,
|
||||||
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
|
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
|
||||||
|
|||||||
@@ -563,7 +563,7 @@ ServerLobbyThread::RegisterTimers()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ServerLobbyThread::HandleRead(SessionId sessionId, const boost::system::error_code& error, size_t bytesRead)
|
ServerLobbyThread::HandleRead(SessionId sessionId, const boost::system::error_code &error, size_t bytesRead)
|
||||||
{
|
{
|
||||||
// Find the session.
|
// Find the session.
|
||||||
SessionWrapper session = m_sessionManager.GetSessionById(sessionId);
|
SessionWrapper session = m_sessionManager.GetSessionById(sessionId);
|
||||||
@@ -949,11 +949,11 @@ ServerLobbyThread::HandleNetPacketCreateGame(SessionWrapper session, const NetPa
|
|||||||
session.playerData->GetUniqueId(),
|
session.playerData->GetUniqueId(),
|
||||||
GetGui(),
|
GetGui(),
|
||||||
m_playerConfig));
|
m_playerConfig));
|
||||||
|
game->Init();
|
||||||
MoveSessionToGame(*game, session);
|
|
||||||
|
|
||||||
// Add game to list of games.
|
// Add game to list of games.
|
||||||
InternalAddGame(game);
|
InternalAddGame(game);
|
||||||
|
|
||||||
|
MoveSessionToGame(*game, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1043,7 +1043,7 @@ ServerLobbyThread::TimerRemoveGame()
|
|||||||
++next;
|
++next;
|
||||||
boost::shared_ptr<ServerGame> tmpGame = i->second;
|
boost::shared_ptr<ServerGame> tmpGame = i->second;
|
||||||
if (!tmpGame->GetSessionManager().HasSessions())
|
if (!tmpGame->GetSessionManager().HasSessions())
|
||||||
InternalRemoveGame(tmpGame); // This will delete the entry from the map.
|
InternalRemoveGame(tmpGame); // This will delete the game.
|
||||||
i = next;
|
i = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1154,6 +1154,7 @@ ServerLobbyThread::InternalRemoveGame(boost::shared_ptr<ServerGame> game)
|
|||||||
// Remove all sessions left in the game.
|
// Remove all sessions left in the game.
|
||||||
game->ResetComputerPlayerList();
|
game->ResetComputerPlayerList();
|
||||||
game->RemoveAllSessions();
|
game->RemoveAllSessions();
|
||||||
|
game->Exit();
|
||||||
// Notify all players.
|
// Notify all players.
|
||||||
boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(game->GetId(), GAME_MODE_CLOSED);
|
boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(game->GetId(), GAME_MODE_CLOSED);
|
||||||
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
|
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ protected:
|
|||||||
|
|
||||||
void InternalListen(unsigned serverPort, bool ipv6, bool sctp);
|
void InternalListen(unsigned serverPort, bool ipv6, bool sctp);
|
||||||
void HandleAccept(boost::shared_ptr<boost::asio::ip::tcp::socket> acceptedSocket,
|
void HandleAccept(boost::shared_ptr<boost::asio::ip::tcp::socket> acceptedSocket,
|
||||||
const boost::system::error_code& error);
|
const boost::system::error_code &error);
|
||||||
|
|
||||||
ServerCallback &GetCallback();
|
ServerCallback &GetCallback();
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ public:
|
|||||||
ServerLobbyThread &lobbyThread, u_int32_t id, const std::string &name, const std::string &pwd, const GameData &gameData, unsigned adminPlayerId, GuiInterface &gui, ConfigFile *playerConfig);
|
ServerLobbyThread &lobbyThread, u_int32_t id, const std::string &name, const std::string &pwd, const GameData &gameData, unsigned adminPlayerId, GuiInterface &gui, ConfigFile *playerConfig);
|
||||||
virtual ~ServerGame();
|
virtual ~ServerGame();
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void Exit();
|
||||||
|
|
||||||
u_int32_t GetId() const;
|
u_int32_t GetId() const;
|
||||||
const std::string &GetName() const;
|
const std::string &GetName() const;
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ protected:
|
|||||||
virtual void Main();
|
virtual void Main();
|
||||||
void RegisterTimers();
|
void RegisterTimers();
|
||||||
|
|
||||||
void HandleRead(SessionId sessionId, const boost::system::error_code& error, size_t bytesRead);
|
void HandleRead(SessionId sessionId, const boost::system::error_code &error, size_t bytesRead);
|
||||||
void HandlePacket(SessionWrapper session, boost::shared_ptr<NetPacket> packet);
|
void HandlePacket(SessionWrapper session, boost::shared_ptr<NetPacket> packet);
|
||||||
void HandleNetPacketInit(SessionWrapper session, const NetPacketInit &tmpPacket);
|
void HandleNetPacketInit(SessionWrapper session, const NetPacketInit &tmpPacket);
|
||||||
void HandleNetPacketAvatarHeader(SessionWrapper session, const NetPacketAvatarHeader &tmpPacket);
|
void HandleNetPacketAvatarHeader(SessionWrapper session, const NetPacketAvatarHeader &tmpPacket);
|
||||||
|
|||||||
Reference in New Issue
Block a user