⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sessionmanager.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//=========== (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 + -