📄 sessionmanager.cpp
字号:
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: Manages and dispatches to all the sessions on the server
//=============================================================================
#include "SessionManager.h"
#include "UserSession.h"
#include "TrackerDatabaseManager.h"
#include "Random.h"
#include "TrackerProtocol.h"
#include "UtlMsgBuffer.h"
#include "../public/ITrackerUserDatabase.h"
#include "../public/ITrackerDistroDatabase.h"
#include "DebugConsole_Interface.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
extern IDebugConsole *g_pConsole;
CSessionManager *g_pSessionManager = NULL;
#define SESSION_INDEX(sessionID) ((sessionID) >> 16)
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSessionManager::CSessionManager() : m_UserSessions(NULL)
{
g_pSessionManager = this;
m_iLockedUserLowerRange = 0;
m_iLockedUserUpperRange = 0;
m_IDMap.SetLessFunc(SessionMapLessFunc);
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CSessionManager::~CSessionManager()
{
if (g_pDataManager)
{
g_pDataManager->deleteThis();
}
g_pSessionManager = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Static function used in session map
//-----------------------------------------------------------------------------
bool CSessionManager::SessionMapLessFunc(SessionMapItem_t const &r1, SessionMapItem_t const &r2)
{
return (r1.userID < r2.userID);
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the session manager
// Input : maxSessions - number of sessions to allocate
//-----------------------------------------------------------------------------
bool CSessionManager::Initialize(unsigned int serverID, int maxSessions, const char *dbsName, CreateInterfaceFn debugFactory)
{
// initialize sleep manager
m_hEvent = Event_CreateEvent();
net()->SetWindowsEvent(m_hEvent);
// allocate space for user data
m_UserSessions.AddMultipleToTail(maxSessions);
m_FreeUserSessions.EnsureCapacity(maxSessions);
// initialize the free list
for (int i = 0; i < m_UserSessions.Size(); i++)
{
m_FreeUserSessions.AddToTail(i);
}
m_iServerID = serverID;
// connect to the data server
g_pDataManager = Create_TrackerDatabaseManager();
if (!g_pDataManager->Initialize(m_iServerID, dbsName, debugFactory))
{
g_pConsole->Print(3, "Could not initialize DataManager\n");
return false;
}
// flush bad users off the server
FlushUsers(true);
return true;
}
//-----------------------------------------------------------------------------
// Purpose: makes session manager not accept any new logins or info requests
//-----------------------------------------------------------------------------
void CSessionManager::LockConnections()
{
m_bLockedDown = true;
}
//-----------------------------------------------------------------------------
// Purpose: Called every 'frame', as often as possible
//-----------------------------------------------------------------------------
bool CSessionManager::RunFrame(bool doIdleWork)
{
// check to see if any of the clients have been connected too long
float time = GetCurrentTime();
bool doneWork = false;
if (doIdleWork)
{
// walk the session map
for (int i = 0; i < m_IDMap.MaxElement(); i++)
{
if (!m_IDMap.IsValidIndex(i))
continue;
CUserSession *session = m_IDMap[i].session;
if (session->State() == USERSESSION_FREE)
{
// they shouldn't be in the mapping, remove
g_pConsole->Print(5, "!! Removing invalid entry from the IDMap (user %d)\n", m_IDMap[i].userID);
m_IDMap.RemoveAt(i);
}
else if (session->CheckForTimeout(time))
{
// we've sent a heartbeat to a Client; Stop checking for now and just continue later
doneWork = true;
break;
}
}
}
if (g_pDataManager->RunFrame())
{
doneWork = true;
}
return doneWork;
}
//-----------------------------------------------------------------------------
// Purpose: Looks up the user session by the userID
//-----------------------------------------------------------------------------
CUserSession *CSessionManager::GetUserSessionByID(unsigned int userID)
{
// search for the item in the mapping
SessionMapItem_t searchItem = { userID, NULL };
int index = m_IDMap.Find(searchItem);
if (m_IDMap.IsValidIndex(index))
{
return m_IDMap[index].session;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Looks the user up in the session list and dispatch the message to them
//-----------------------------------------------------------------------------
void CSessionManager::DispatchMessageToUserSession(IReceiveMessage *userMsg)
{
if (m_bLockedDown)
{
// don't accept any more messages from users when locked down
return;
}
// session index into the session array is the high word of the sessionID
unsigned short sessionIndex = SESSION_INDEX(userMsg->SessionID());
bool bBadPacket = false;
if (sessionIndex >= m_UserSessions.Size())
{
// bad packet
bBadPacket = true;
}
else if (m_UserSessions[sessionIndex].SessionID() == userMsg->SessionID())
{
// sessionID is correct
float timer = GetCurrentTime();
if (m_UserSessions[sessionIndex].State() == USERSESSION_ACTIVE)
{
m_UserSessions[sessionIndex].ReceivedMessage(userMsg, timer);
}
else if (m_UserSessions[sessionIndex].State() == USERSESSION_CONNECTING && userMsg->GetMsgID() == TCLS_RESPONSE)
{
m_UserSessions[sessionIndex].ReceivedMessage(userMsg, timer);
}
else
{
bBadPacket = true;
}
if (m_UserSessions[sessionIndex].State() == USERSESSION_CONNECTFAILED)
{
FreeUserSession(&m_UserSessions[sessionIndex]);
}
}
else
{
bBadPacket = true;
g_pConsole->Print(6, "**** !! Received mismatched sessionID (%d, should be %d!\n", userMsg->SessionID(), m_UserSessions[sessionIndex].SessionID());
}
if (bBadPacket)
{
if (userMsg->GetMsgID() == TCLS_HEARTBEAT || userMsg->GetMsgID() == TCLS_MESSAGE || userMsg->GetMsgID() == TCLS_ROUTETOFRIEND)
{
// send back a message telling this Client that they're just not logged in
g_pConsole->Print(6, "Bad pack, sending disconnect msg\n");
ISendMessage *msg = net()->CreateReply(TSVC_DISCONNECT, userMsg);
msg->SetSessionID(userMsg->SessionID());
msg->WriteInt("minTime", 1);
msg->WriteInt("maxTime", 4);
net()->SendMessage(msg, NET_RELIABLE);
}
g_pConsole->Print(5, "Dropped msg '%d': Bad sessionID %d (%s)\n", userMsg->GetMsgID(), userMsg->SessionID(), userMsg->NetAddress().ToStaticString());
}
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a new user session from the free list, and sets up
// a new session ID for it
//-----------------------------------------------------------------------------
CUserSession *CSessionManager::GetNewUserSession( void )
{
// take a new session from the front of the free list
int freeListIndex = m_FreeUserSessions.Head();
int arrayIndex = m_FreeUserSessions[freeListIndex];
m_FreeUserSessions.Remove(freeListIndex);
CUserSession *newSession = &m_UserSessions[arrayIndex];
// increment the new sessions sessionID so that no old messages will be valid for it
newSession->CreateNewSessionID(arrayIndex);
// return the new session
return newSession;
}
//-----------------------------------------------------------------------------
// Purpose: Frees a user session and moves it into the free list
//-----------------------------------------------------------------------------
void CSessionManager::FreeUserSession(CUserSession *userSession)
{
if (userSession->UID() > 0)
{
UnmapUserID(userSession->UID());
}
// clear the freed session
userSession->Clear();
// add it to the free list
unsigned short sessionIndex = SESSION_INDEX(userSession->SessionID());
m_FreeUserSessions.AddToTail((unsigned int)sessionIndex);
}
//-----------------------------------------------------------------------------
// Purpose: locks a specified user range; disconects and prevents connection from any users in that range
//-----------------------------------------------------------------------------
void CSessionManager::LockUserRange(unsigned int lowerRange, unsigned int upperRange)
{
m_iLockedUserLowerRange = lowerRange;
m_iLockedUserUpperRange = upperRange;
// disconnect all users in that range
for (unsigned int userID = m_iLockedUserLowerRange; userID <= m_iLockedUserUpperRange; userID++)
{
SessionMapItem_t searchItem = { userID, NULL };
int index = m_IDMap.Find(searchItem);
if (m_IDMap.IsValidIndex(index))
{
m_IDMap[index].session->Logoff(true, false, false);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: unlocks any previous locked
//-----------------------------------------------------------------------------
void CSessionManager::UnlockUserRange()
{
m_iLockedUserLowerRange = 0;
m_iLockedUserUpperRange = 0;
// reload the user distribution table
g_pDataManager->ReloadUserDistribution();
}
//-----------------------------------------------------------------------------
// Purpose: forces the user to be disconnected, and ensures the are logged out of sqldb
//-----------------------------------------------------------------------------
void CSessionManager::ForceDisconnectUser(unsigned int userID)
{
// find the session
CUserSession *userSession = sessionmanager()->GetUserSessionByID(userID);
if (userSession)
{
// we have the user, log them out
userSession->Logoff(true, false, true);
}
else
{
// user not found; kill them out of database
ITrackerUserDatabase *db = g_pDataManager->TrackerUser(userID);
if (db)
{
db->User_Logoff(NULL, userID, true);
// walk through, removing this user as a watcher
int dbCount = g_pDataManager->TrackerUserCount();
for (int i = 0; i < dbCount; i++)
{
g_pDataManager->TrackerUserByIndex(i).db->User_RemoveWatcher(userID);
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: makes a user check their messages
//-----------------------------------------------------------------------------
void CSessionManager::UserCheckMessages(unsigned int userID)
{
CUserSession *userSession = sessionmanager()->GetUserSessionByID(userID);
if (userSession)
{
// we have the user, make them check messages
userSession->CheckForMessages();
}
}
//-----------------------------------------------------------------------------
// Purpose: queues a network message to be sent to a user
//-----------------------------------------------------------------------------
void CSessionManager::SendMessageToUser(unsigned int userID, unsigned int sessionID, int msgID, void const *message, int messageSize)
{
// find the session
unsigned short sessionIndex = SESSION_INDEX(sessionID);
// check for valid sessionIndex
if (sessionIndex >= m_UserSessions.Size())
{
g_pConsole->Print(5, "SendMessageToUser() : out-of-range sessionID(%d) sending msg (%d) to user (%d)\n", sessionID, msgID, userID);
SaveUnroutableNetworkMessage(userID, msgID, message, messageSize);
return;
}
CUserSession &session = m_UserSessions[sessionIndex];
// check the session is valid
if (!session.State() == USERSESSION_ACTIVE)
{
g_pConsole->Print(5, "SendMessageToUser() : invalid sessionID(%d) sending msg (%d) to user (%d)\n", sessionID, msgID, userID);
SaveUnroutableNetworkMessage(userID, msgID, message, messageSize);
return;
}
// check the userID matches the sessionID
if (session.UID() != userID)
{
g_pConsole->Print(5, "SendMessageToUser() : invalid target user (%d) with valid sessionID(%d) sending msg (%d)\n", userID, sessionID, msgID);
SaveUnroutableNetworkMessage(userID, msgID, message, messageSize);
return;
}
// get their network address
CNetAddress addr(session.IP(), session.Port());
// convert the data block into a message
ISendMessage *msg = net()->CreateMessage(msgID, message, messageSize);
msg->SetNetAddress(addr);
msg->SetEncrypted(true);
msg->SetSessionID(sessionID);
// send the message along to the user
session.SendMessage(msg, NET_RELIABLE);
}
//-----------------------------------------------------------------------------
// Purpose: saves a message that could not be routed to the target user
//-----------------------------------------------------------------------------
void CSessionManager::SaveUnroutableNetworkMessage(unsigned int userID, int msgID, void const *message, int messageSize)
{
// only save text chat messages
if (msgID == TCL_MESSAGE)
{
// get enough info to save the message
bool bSaveSuccessful = false;
CUserSession *targetUser = GetUserSessionByID(userID);
if (targetUser)
{
// post the message into the database
unsigned int fromID;
char messageText[512];
CUtlMsgBuffer msg(message, messageSize);
msg.ReadUInt("uid", fromID);
msg.ReadString("Text", messageText, sizeof(messageText));
targetUser->PostMessageToUser(fromID, userID, messageText, 0);
bSaveSuccessful = true;
}
if (!bSaveSuccessful)
{
g_pConsole->Print(6, "** Unroutable chat message to (%d) successfully saved in db\n", userID);
}
else
{
g_pConsole->Print(8, "!!!! Throwing away chat message to (%d)\n", userID);
}
}
else
{
g_pConsole->Print(8, "!!!! Throwing away unroutable non-chat message to (%d)\n", userID);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -