Reorganized and cleaned up the solution.

This commit is contained in:
HikikoMarmy
2026-03-02 12:37:07 +00:00
parent 8012f30170
commit d4dfbddf69
175 changed files with 1516 additions and 1136 deletions

View File

@@ -0,0 +1,203 @@
/*
* CharacterSaveManager is responsible for managing character saves while online.
* Because Return To Arms can have any user in the party initiate a save for any
* other user in the party, we need to keep track of these tasks.
*
* Tasks keep track of the user that initiated the save (the owner),
* and the target user whose character is being saved.
*/
#include "Game/CharacterSaveManager.hpp"
#include "Game/RealmUser.hpp"
#include "Game/RealmUserManager.hpp"
#include "Database/Database.hpp"
#include "logging.hpp"
CharacterSaveManager::CharacterSaveManager()
{
m_tasks.clear();
}
CharacterSaveManager::~CharacterSaveManager()
{
m_tasks.clear();
}
bool CharacterSaveManager::BeginSaveTask(
const sptr_user user,
const uint32_t characterId,
const CharacterSlotData &metaData,
const CharacterSaveType saveType )
{
return BeginSaveTask( user, user, characterId, metaData, saveType );
}
bool CharacterSaveManager::BeginSaveTask(
const sptr_user m_owner,
const sptr_user m_target,
const uint32_t characterId,
const CharacterSlotData &metaData,
const CharacterSaveType saveType )
{
if( !m_owner || !m_target )
{
Log::Error( "CreateSaveTask called with empty session IDs!" );
return false;
}
const auto &sessionId = m_owner->m_sessionId;
if( m_tasks.find( sessionId ) != m_tasks.end() )
{
m_tasks.erase( sessionId );
}
auto task = std::make_shared< CharacterSaveTask >( saveType, characterId );
task->m_ownerUser = m_owner;
task->m_targetUser = m_target;
task->m_meta = metaData;
m_tasks[ sessionId ] = task;
return true;
}
void CharacterSaveManager::AppendSaveData( const std::wstring &sessionId, const std::vector<uint8_t> &data, bool endOfData )
{
try
{
auto it = m_tasks.find( sessionId );
if( it == m_tasks.end() )
{
Log::Error( "AddDataToSaveTask: No task found for session ID [{}].", sessionId );
return;
}
auto &task = it->second;
if( !task->AppendData( data ) )
{
Log::Error( "AddDataToSaveTask: Append failed for session ID [{}].", sessionId );
return;
}
if( !endOfData )
{
return;
}
if( !task->Validate() )
{
Log::Error( "AddDataToSaveTask: Validation failed for session ID [{}].", sessionId );
return;
}
if( !CommitSaveTask( sessionId ) )
{
Log::Error( "AddDataToSaveTask: Commit failed for session ID [{}].", sessionId );
return;
}
Log::Debug( "AddDataToSaveTask: Final chunk committed for session ID [{}].", sessionId );
}
catch( const std::exception &e )
{
Log::Error( "AddDataToSaveTask: Exception for session ID [{}]: {}", sessionId, e.what() );
}
}
bool CharacterSaveManager::CommitSaveTask( const std::wstring &sessionId )
{
try
{
auto node = m_tasks.extract( sessionId );
if( !node )
{
Log::Error( "CommitSaveTask: Task for session ID [{}] not found.", sessionId );
return false;
}
const auto &saveTask = node.mapped();
if( !saveTask->Validate() )
{
return false;
}
auto &targetUser = saveTask->m_targetUser;
if( !targetUser )
{
Log::Error( "CommitSaveTask: Target user not found." );
return false;
}
if( saveTask->m_saveType == CharacterSaveType::NEW_CHARACTER )
{
auto result = Database::Get().CreateNewCharacter( targetUser->m_accountId, saveTask->m_meta, saveTask->m_data );
if( !result )
{
Log::Error( "CommitSaveTask: Failed to create new character for account ID: {}", targetUser->m_accountId );
return false;
}
Log::Debug( "CommitSaveTask: New character created with ID {} for account ID: {}", result, targetUser->m_accountId );
targetUser->m_characterId = result;
}
else
{
if( 0 == saveTask->m_characterId )
{
Log::Error( "CommitSaveTask: Invalid character ID for save task." );
return false;
}
auto result = Database::Get().SaveCharacter( targetUser->m_accountId, targetUser->m_characterId, saveTask->m_meta, saveTask->m_data );
if( !result )
{
Log::Error( "CommitSaveTask: Failed to save character for account ID: {}", targetUser->m_accountId );
return false;
}
Log::Debug( "CommitSaveTask: Character saved for account ID: {}", targetUser->m_accountId );
}
}
catch( const std::exception &e )
{
Log::Error( "CommitSaveTask: Exception occurred while committing task for session ID [{}]: {}", sessionId, e.what() );
return false;
}
Log::Debug( "CommitSaveTask: Task for session ID [{}] committed successfully.", sessionId );
return true;
}
void CharacterSaveManager::RemoveSaveTask( const std::wstring &sessionId )
{
auto it = m_tasks.find( sessionId );
if( it != m_tasks.end() )
{
m_tasks.erase( it );
Log::Debug( "RemoveSaveTask: Task for session ID [{}] removed.", sessionId );
}
else
{
Log::Error( "RemoveSaveTask: Task for session ID [{}] not found.", sessionId );
}
}
sptr_character_save_task CharacterSaveManager::FindSaveTask( const std::wstring &sessionId )
{
auto it = m_tasks.find( sessionId );
if( it != m_tasks.end() )
{
return it->second;
}
else
{
Log::Error( "FindSaveTask: Task for session ID [{}] not found.", sessionId );
return nullptr;
}
}

76
Source/Game/ChatRoom.cpp Normal file
View File

@@ -0,0 +1,76 @@
#include "Game/ChatRoomSession.hpp"
#include "Game/RealmUser.hpp"
ChatRoomSession::ChatRoomSession()
{
m_type = RoomType::Public;
m_index = 0;
m_name.clear();
}
ChatRoomSession::~ChatRoomSession()
{
m_type = RoomType::Public;
m_index = 0;
m_name.clear();
}
bool ChatRoomSession::AddMember( sptr_user user )
{
if( !user )
return false;
for( const auto &member : m_members )
{
if( member.lock() == user )
return false; // User already in the room.
}
m_members.push_back( user );
return true;
}
bool ChatRoomSession::RemoveMember( sptr_user user )
{
if( !user )
return false;
auto it = std::remove_if( m_members.begin(), m_members.end(),
[ &user ]( const std::weak_ptr< RealmUser > &member )
{
return member.lock() == user;
} );
if( it == m_members.end() )
{
return false;
}
m_members.erase( it, m_members.end() );
return true;
}
bool ChatRoomSession::IsMember( sptr_user user )
{
if( !user )
return false;
for( const auto &member : m_members )
{
if( member.lock() == user )
return true;
}
return false;
}
bool ChatRoomSession::IsPublic() const
{
return m_type == RoomType::Public;
}
bool ChatRoomSession::IsPrivate() const
{
return m_type == RoomType::Private;
}

View File

@@ -0,0 +1,294 @@
#include "Game/ChatRoomManager.hpp"
#include "Game/RealmUser.hpp"
#include "Network/Event/NotifyRoomMessage.hpp"
#include "logging.hpp"
ChatRoomManager::ChatRoomManager()
{
m_chatSessionList.clear();
m_roomIndex = 0;
CreatePublicRooms();
}
ChatRoomManager::~ChatRoomManager()
{
}
void ChatRoomManager::CreatePublicRooms()
{
std::vector< std::wstring > RoomNames = {
L"Champions Reborn",
L"Adventurous",
L"Courageous",
L"Champion",
L"Legendary",
L"Epic"
};
for( const auto &name : RoomNames )
{
auto new_session = std::make_shared< ChatRoomSession >();
new_session->m_name = name;
new_session->m_type = ChatRoomSession::RoomType::Public;
new_session->m_index = m_roomIndex;
new_session->m_banner = L"";
m_chatSessionList[ m_roomIndex++ ] = new_session;
}
}
std::vector<sptr_chat_room_session> ChatRoomManager::GetPublicRoomList() const
{
std::vector< sptr_chat_room_session > result;
for( const auto &chatSession : m_chatSessionList )
{
if( chatSession.second->m_type == ChatRoomSession::RoomType::Public )
{
result.push_back( chatSession.second );
}
}
return result;
}
bool ChatRoomManager::JoinRoom( sptr_user user, const std::wstring &roomName )
{
if( roomName.empty() || !user )
return false;
auto chatSession = FindRoom( roomName );
if( !chatSession )
{
Log::Error( "Chat room [{}] not found", roomName );
return false;
}
if( !chatSession->AddMember( user ) )
{
Log::Error( "Failed to add user [{}] to chat room [{}]", user->m_username, roomName );
return false;
}
if( chatSession->m_type == ChatRoomSession::RoomType::Public )
{
user->m_publicRoomId = chatSession->m_index;
}
else
{
user->m_privateRoomId = chatSession->m_index;
SendMessageToRoom( roomName, L"", L"User '" + user->m_chatHandle + L"' has joined the room." );
}
Log::Info( "User [{}] joined chat room [{}]", user->m_username, roomName );
return true;
}
bool ChatRoomManager::LeaveRoom( sptr_user user, const std::wstring &roomName )
{
if( !user || roomName.empty() )
return false;
auto chatSession = FindRoom( roomName );
if( !chatSession )
{
Log::Error( "Chat room [{}] not found", roomName );
return false;
}
if( !chatSession->RemoveMember( user ) )
{
Log::Error( "Failed to remove user [{}] from chat room [{}]", user->m_username, roomName );
return false;
}
if( chatSession->m_type == ChatRoomSession::RoomType::Private )
{
if( chatSession->m_members.empty() && chatSession->m_moderators.empty() )
{
m_chatSessionList.erase( chatSession->m_index );
Log::Debug( "Private chat room [{}] deleted", roomName );
}
user->m_privateRoomId = -1;
}
else
{
user->m_publicRoomId = -1;
}
Log::Info( "User [{}] left chat room [{}]", user->m_username, roomName );
return true;
}
bool ChatRoomManager::LeaveRoom( sptr_user user, const int32_t roomId )
{
if( !user || roomId < 0 )
return false;
const auto chatSession = FindRoom( roomId );
if( !chatSession )
{
Log::Error( "Chat room with ID [{}] not found", roomId );
return false;
}
if( !chatSession->RemoveMember( user ) )
{
Log::Error( "Failed to remove user [{}] from chat room with ID [{}]", user->m_username, roomId );
return false;
}
if( chatSession->m_type == ChatRoomSession::RoomType::Private )
{
if( chatSession->m_members.empty() && chatSession->m_moderators.empty() )
{
m_chatSessionList.erase( roomId );
Log::Info( "Private chat room with ID [{}] deleted", roomId );
}
user->m_privateRoomId = -1;
}
else
{
user->m_publicRoomId = -1;
}
Log::Info( "User [{}] left chat room with ID [{}]", user->m_username, roomId );
return true;
}
void ChatRoomManager::OnDisconnectUser( sptr_user user )
{
if( !user ) return;
LeaveRoom( user, user->m_publicRoomId );
LeaveRoom( user, user->m_privateRoomId );
}
bool ChatRoomManager::CreateGameChatSession( sptr_user owner, std::wstring roomName )
{
for( const auto &chatSession : m_chatSessionList )
{
if( chatSession.second->m_name == roomName )
{
Log::Error( "Chat Room name is already in use! [{}]", roomName );
return false;
}
}
auto new_session = std::make_shared< ChatRoomSession >();
new_session->m_type = ChatRoomSession::RoomType::Private;
new_session->m_name = roomName;
new_session->m_owner = owner;
new_session->m_banner = L"";
new_session->m_index = m_roomIndex;
new_session->AddMember( owner );
owner->m_privateRoomId = m_roomIndex;
m_chatSessionList[ m_roomIndex++ ] = new_session;
return true;
}
bool ChatRoomManager::CloseGameChatSession( const std::wstring roomName )
{
if( roomName.empty() )
return false;
const auto it = std::find_if( m_chatSessionList.begin(), m_chatSessionList.end(),
[ &roomName ]( const auto &pair )
{
return pair.second->m_name == roomName;
} );
if( it == m_chatSessionList.end() )
{
Log::Error( "Chat room [{}] not found", roomName );
return false;
}
auto &chatSession = it->second;
if( chatSession->m_type != ChatRoomSession::RoomType::Private )
{
Log::Error( "Chat room [{}] is not a private room", roomName );
return false;
}
for( const auto &member : chatSession->m_members )
{
if( auto user = member.lock() )
{
user->m_privateRoomId = -1;
}
}
m_chatSessionList.erase( it );
Log::Info( "Chat room [{}] closed", roomName );
return true;
}
void ChatRoomManager::SendMessageToRoom( std::wstring roomName, std::wstring handle, std::wstring message )
{
if( roomName.empty() || message.empty() )
return;
auto chatSession = FindRoom( roomName );
if( !chatSession )
{
Log::Error( "Chat room [{}] not found", roomName );
return;
}
NotifyRoomMessage notifyMessage( roomName, handle, message );
for( const auto &m : chatSession->m_members )
{
if( auto member = m.lock() )
{
member->sock->send( notifyMessage );
}
}
}
sptr_chat_room_session ChatRoomManager::FindRoom( const std::wstring &gameName )
{
if( gameName.empty() )
return nullptr;
for( const auto &chatSession : m_chatSessionList )
{
if( chatSession.second->m_name == gameName )
{
return chatSession.second;
}
}
return nullptr;
}
sptr_chat_room_session ChatRoomManager::FindRoom( const int32_t roomId )
{
if( roomId < 0 )
return nullptr;
auto it = m_chatSessionList.find( roomId );
if( it != m_chatSessionList.end() )
{
return it->second;
}
Log::Error( "Chat room with ID [{}] not found", roomId );
return nullptr;
}

186
Source/Game/GameSession.cpp Normal file
View File

@@ -0,0 +1,186 @@
#include "Game/GameSession.hpp"
#include "Game/RealmUser.hpp"
#include "logging.hpp"
GameSession::GameSession( uint32_t index ) : m_gameId( index )
{
m_members.fill( std::weak_ptr< RealmUser >() );
m_type = GameType::Public;
m_state = GameState::NotReady;
m_currentPlayers = 0;
m_maximumPlayers = 4;
m_difficulty = 0;
m_gameMode = 0;
m_mission = 0;
m_unknown = 0;
m_networkSave = 0;
m_hostNatPort = 0;
m_hostLocalPort = 0;
m_hostLocalAddr.clear();
m_hostExternalAddr.clear();
m_gameName.clear();
m_ownerName.clear();
m_gameData.clear();
m_description.clear();
}
GameSession::~GameSession()
{
m_members.fill( std::weak_ptr< RealmUser >() );
m_gameId = 0;
m_type = GameType::Public;
m_state = GameState::NotReady;
m_currentPlayers = 0;
m_maximumPlayers = 0;
m_difficulty = 0;
m_gameMode = 0;
m_mission = 0;
m_unknown = 0;
m_networkSave = 0;
m_hostNatPort = 0;
m_hostLocalAddr.clear();
m_hostExternalAddr.clear();
m_gameName.clear();
m_ownerName.clear();
m_gameData.clear();
m_description.clear();
}
bool GameSession::IsJoinable( sptr_user user ) const
{
if( user )
{
for( const auto &m : m_members )
{
if( m.expired() )
continue;
const auto &member = m.lock();
if( member == user )
{
return false;
}
}
}
return ( m_state == GameState::Open && m_currentPlayers < m_maximumPlayers );
}
sptr_user GameSession::GetOwner() const
{
if( !m_members[ 0 ].expired() )
{
return m_members[ 0 ].lock();
}
return nullptr;
}
sptr_user GameSession::GetMember( int32_t index ) const
{
if( index < 0 || index >= static_cast< int32_t >( m_members.size() ) )
return nullptr;
if( !m_members[ index ].expired() )
{
return m_members[ index ].lock();
}
return nullptr;
}
sptr_user GameSession::GetMemberBySessionId( const std::wstring &sessionId ) const
{
for( const auto &m : m_members )
{
if( m.expired() )
continue;
const auto &member = m.lock();
if( member->m_sessionId == sessionId )
{
return member;
}
}
return nullptr;
}
std::vector<sptr_user> GameSession::GetMembers() const
{
std::vector< sptr_user > members;
for( const auto &m : m_members )
{
if( !m.expired() )
{
members.push_back( m.lock() );
}
}
return members;
}
bool GameSession::AddMember( sptr_user user )
{
if( !user ) return false;
int8_t freeIndex = -1;
for( int8_t i = 0; i < static_cast< int8_t >( m_members.size() ); ++i )
{
auto memberPtr = m_members[ i ].lock();
if( memberPtr )
{
if( memberPtr->m_sessionId == user->m_sessionId )
return false;
}
else if( freeIndex == -1 )
{
freeIndex = i;
}
}
if( freeIndex == -1 )
{
Log::Error( "Game session is full! [{}]", m_gameName );
return false;
}
user->m_gameId = m_gameId;
m_members[ freeIndex ] = user;
m_currentPlayers++;
Log::Info( "Added user [{}] to game session [{}] at index {}",
user->m_username, m_gameName, freeIndex );
return true;
}
bool GameSession::RemoveMember( sptr_user user )
{
if( !user ) return false;
for( int8_t i = 0; i < static_cast< int8_t >( m_members.size() ); ++i )
{
auto memberPtr = m_members[ i ].lock();
if( memberPtr && memberPtr == user )
{
user->m_gameId = -1;
m_members[ i ].reset();
break;
}
}
m_currentPlayers--;
Log::Info( "Removed user [{}] from game session [{}]", user->m_username, m_gameName );
return true;
}

View File

@@ -0,0 +1,422 @@
#include "Game/GameSessionManager.hpp"
#include "Game/RealmUser.hpp"
#include "Network/Event/NotifyGameDiscovered.hpp"
#include "Network/Event/NotifyClientDiscovered.hpp"
#include "Network/Event/NotifyClientDiscovered_RTA.hpp"
#include "Network/Event/NotifyClientRequestConnect.hpp"
#include "Network/Event/NotifyClientRequestConnect_RTA.hpp"
#include "logging.hpp"
GameSessionManager::GameSessionManager()
{
m_uniqueGameIndex = 0;
m_gameSessionList[ 0 ].clear();
m_gameSessionList[ 1 ].clear();
}
GameSessionManager::~GameSessionManager()
{
}
void GameSessionManager::OnDisconnectUser( sptr_user user )
{
if( !user || user->m_gameId < 0 )
return;
const auto gameId = user->m_gameId;
const auto gameType = user->m_gameType;
auto session = FindGame( gameId, gameType );
if( !session )
return;
const auto owner = session->GetOwner();
if( !owner )
{
Log::Error( "Game session owner not found! [{}]", gameId );
ForceTerminateGame( gameId, gameType );
return;
}
if( owner->m_sessionId == user->m_sessionId )
{
Log::Info( "Game session owner disconnected! [{}]", gameId );
ForceTerminateGame( gameId, gameType );
}
}
bool GameSessionManager::CreateGameSession_CON( sptr_user user,
const std::wstring gameInfo,
const std::wstring name,
const std::wstring stage,
bool isPrivateGame )
{
if( name.empty() )
{
Log::Error( "Invalid parameters for creating game session!" );
return false;
}
auto new_session = std::make_shared< GameSession >( m_uniqueGameIndex++ );
if( isPrivateGame )
{
new_session->m_type = GameSession::GameType::Private;
new_session->m_gameName = name;
}
else
{
new_session->m_type = GameSession::GameType::Public;
new_session->m_gameName = name + L" [" + stage + L"]";
}
user->m_isHost = true;
user->m_gameId = new_session->m_gameId;
user->m_discoveryAddr = "";
user->m_discoveryPort = 0;
new_session->AddMember( user );
std::lock_guard< std::mutex > lock( m_dataMutex );
m_gameSessionList[ RealmGameType::CHAMPIONS_OF_NORRATH ].push_back( new_session );
return true;
}
bool GameSessionManager::CreateGameSession_RTA(
sptr_user user,
const std::wstring gameInfo,
const std::wstring name,
const std::array< int8_t, 5 > &attributes,
const bool isPrivateGame )
{
if( nullptr != FindGame( name, RealmGameType::RETURN_TO_ARMS ) )
{
Log::Error( "Game name is already in use! [{}]", name );
return false;
}
auto new_session = std::make_shared< GameSession >( m_uniqueGameIndex++ );
if( isPrivateGame )
{
new_session->m_type = GameSession::GameType::Private;
}
else
{
new_session->m_type = GameSession::GameType::Public;
new_session->m_gameData = Util::WideToUTF8( gameInfo );
new_session->m_difficulty = attributes[ 0 ];
new_session->m_gameMode = attributes[ 1 ];
new_session->m_unknown = attributes[ 2 ];
new_session->m_mission = attributes[ 3 ];
new_session->m_networkSave = attributes[ 4 ];
}
new_session->m_gameName = name;
user->m_isHost = true;
user->m_gameId = new_session->m_gameId;
user->m_discoveryAddr = "";
user->m_discoveryPort = 0;
new_session->AddMember( user );
std::lock_guard< std::mutex > lock( m_dataMutex );
m_gameSessionList[ RealmGameType::RETURN_TO_ARMS ].push_back( new_session );
return true;
}
bool GameSessionManager::ForceTerminateGame( int32_t gameId, RealmGameType clientType )
{
if( gameId < 0 )
{
return false;
}
std::lock_guard< std::mutex > lock( m_dataMutex );
const auto &gameList = m_gameSessionList[ clientType ];
const auto it = std::find_if( gameList.begin(), gameList.end(), [ &gameId ]( sptr_game_session gameSession )
{
return gameSession->m_gameId == gameId;
} );
if( it != m_gameSessionList[ clientType ].end() )
{
m_gameSessionList[ clientType ].erase( it );
return true;
}
return false;
}
sptr_game_session GameSessionManager::FindGame( const int32_t gameId, const RealmGameType gameType )
{
if( gameId < 0 ) return nullptr;
std::lock_guard< std::mutex > lock( m_dataMutex );
for( auto &gameSession : m_gameSessionList[ gameType ] )
{
if( gameSession->m_gameId == gameId )
{
return gameSession;
}
}
return nullptr;
}
sptr_game_session GameSessionManager::FindGame( const std::wstring &gameName, const RealmGameType gameType )
{
if( gameName.empty() ) return nullptr;
std::lock_guard< std::mutex > lock( m_dataMutex );
for( auto &gameSession : m_gameSessionList[ gameType ] )
{
if( gameSession->m_gameName == gameName )
{
return gameSession;
}
}
return nullptr;
}
bool GameSessionManager::RequestOpen( sptr_user user )
{
auto gameId = user->m_gameId;
auto gameType = user->m_gameType;
auto session = FindGame( gameId, gameType );
if( session == nullptr )
{
Log::Error( "Game session not found! [{}]", gameId );
return false;
}
if( session->m_state == GameSession::GameState::Open )
{
return false;
}
if( user->m_discoveryAddr.empty() )
{
Log::Error( "User discovery address is empty! [{}]", gameId );
return false;
}
session->m_hostLocalAddr = user->m_localAddr;
session->m_hostLocalPort = user->m_localPort;
session->m_hostExternalAddr = user->m_discoveryAddr;
session->m_hostNatPort = user->m_discoveryPort;
session->m_state = GameSession::GameState::Open;
// Tell the host its own address.
user->sock->send( NotifyGameDiscovered( user ) );
Log::Info( "Game Session [{}] Discoverable on {}", gameId, user->m_discoveryAddr );
return true;
}
bool GameSessionManager::RequestCancel( sptr_user user )
{
if( !user || user->m_gameId < 0 )
return false;
std::lock_guard<std::mutex> lock( m_dataMutex );
const auto gameId = user->m_gameId;
const auto gameType = user->m_gameType;
auto &gameList = m_gameSessionList[ gameType ];
const auto it = std::find_if( gameList.begin(), gameList.end(),
[ gameId ]( const sptr_game_session &gameSession )
{
return gameSession->m_gameId == gameId;
} );
if( it == gameList.end() )
return false;
const auto &session = *it;
if( false == session->RemoveMember( user ) )
{
Log::Error( "Failed to remove user [{}] from game session [{}]", user->m_username, gameId );
}
if( session->m_currentPlayers <= 0 )
{
Log::Info( "Game session [{}] is empty, removing it", gameId );
gameList.erase( it );
}
else
{
Log::Info( "User [{}] left game session [{}], remaining players: {}",
user->m_username, gameId, session->m_currentPlayers );
}
return true;
}
bool GameSessionManager::RequestJoin( sptr_user join_user )
{
const auto gameId = join_user->m_gameId;
const auto gameType = join_user->m_gameType;
auto session = FindGame( gameId, gameType );
if( session == nullptr )
{
Log::Error( "Game session not found! [{}]", gameId );
return false;
}
if( session->m_state != GameSession::GameState::Open )
{
Log::Error( "Game session not open! [{}]", gameId );
return false;
}
auto host_user = session->GetOwner();
if( host_user == nullptr )
{
Log::Error( "Host not found! [{}]", gameId );
ForceTerminateGame( gameId, gameType );
return false;
}
if( host_user->m_discoveryAddr.empty() )
{
Log::Error( "User discovery address is empty! [{}]", gameId );
ForceTerminateGame( gameId, gameType );
return false;
}
if( host_user->m_gameType == RealmGameType::CHAMPIONS_OF_NORRATH )
{
ProcessJoinNorrath( join_user, host_user );
}
else
{
ProcessJoinArms( join_user, host_user );
}
Log::Info( "User [{}] Joining game session... [{}]", join_user->m_sessionId, gameId );
return true;
}
// NOTE:
// Request Start seems to be for RTA only.
// CON will disconnect from the server at start time.
bool GameSessionManager::RequestStart( sptr_user user )
{
const auto gameId = user->m_gameId;
const auto gameType = user->m_gameType;
auto session = FindGame( gameId, gameType );
if( session == nullptr )
{
Log::Error( "Game session not found! [{}]", gameId );
return false;
}
std::lock_guard<std::mutex> lock( m_dataMutex );
session->m_state = GameSession::GameState::Started;
// Remove the game from the list.
auto &gameList = m_gameSessionList[ gameType ];
const auto it = std::find_if( gameList.begin(), gameList.end(),
[ gameId ]( const sptr_game_session &gameSession )
{
return gameSession->m_gameId == gameId;
} );
if( it != gameList.end() )
{
gameList.erase( it );
}
Log::Info( "Game session [{}] started", gameId );
return true;
}
std::vector<sptr_game_session> GameSessionManager::GetAvailableGameSessionList( const RealmGameType gameType ) const
{
std::lock_guard<std::mutex> lock( m_dataMutex );
std::vector<sptr_game_session> list;
for( const auto &game : m_gameSessionList[ gameType ] )
{
if( game->m_type == GameSession::GameType::Public &&
game->m_state == GameSession::GameState::Open )
{
list.push_back( game );
}
}
return list;
}
std::vector<sptr_game_session> GameSessionManager::GetPublicGameSessionList( const RealmGameType gameType ) const
{
std::lock_guard<std::mutex> lock( m_dataMutex );
std::vector<sptr_game_session> list;
for( const auto &game : m_gameSessionList[ gameType ] )
{
if( game->m_type == GameSession::GameType::Public )
list.push_back( game );
}
return list;
}
std::vector<sptr_game_session> GameSessionManager::GetPrivateGameSessionList( const RealmGameType gameType ) const
{
std::lock_guard<std::mutex> lock( m_dataMutex );
std::vector<sptr_game_session> list;
for( const auto &game : m_gameSessionList[ gameType ] )
{
if( game->m_type == GameSession::GameType::Private )
list.push_back( game );
}
return list;
}
void GameSessionManager::ProcessJoinNorrath( sptr_user join, sptr_user host )
{
// Tell the joining user its own address
join->sock->send( NotifyClientDiscovered( join ) );
// Tell the host the joining user's address.
host->sock->send( NotifyClientRequestConnect( join ) );
}
void GameSessionManager::ProcessJoinArms( sptr_user join, sptr_user host )
{
Log::Debug("Join User IPs : [{}:{}] -> [{}:{}]",
join->m_localAddr, join->m_localPort, join->m_discoveryAddr, join->m_discoveryPort );
Log::Debug("Host User IPs : [{}:{}] -> [{}:{}]",
host->m_localAddr, host->m_localPort, host->m_discoveryAddr, host->m_discoveryPort );
// Tell the joining user its own address
join->sock->send( NotifyClientDiscovered_RTA( join ) );
// Tell the host the joining user's address.
host->sock->send( NotifyClientRequestConnect_RTA( join ) );
}

View File

@@ -0,0 +1,659 @@
#include "Game/RealmCharacter.hpp"
#include "Game/CharacterSaveTask.hpp"
#include "Common/ByteBufferReader.hpp"
#include "Common/RLEZ.hpp"
#include "logging.hpp"
RealmCharacter::RealmCharacter()
{
m_characterId = 0;
m_data.clear();
Initialize();
}
RealmCharacter::RealmCharacter( const int32_t character_id, const CharacterSlotData meta, const std::vector<uint8_t> data )
{
m_characterId = character_id;
m_data = data;
m_metaData = meta;
Initialize();
}
RealmCharacter::~RealmCharacter()
{
}
void RealmCharacter::Initialize()
{
name = "";
for( auto &val : unknown_000 )
{
val = 0;
}
unknown_str = "";
for( auto &val : unknown_004 )
{
val = 0;
}
character_class = CharacterClass::WARRIOR;
character_race = CharacterRace::BARBARIAN_M;
current_level = 0;
pending_level = 0;
unknown_009 = 0;
experience = 0;
std::memset( unknown_010, 0, sizeof( unknown_010 ) );
std::memset( stats, 0, sizeof( stats ) );
unknown_016 = 0;
current_hp = 0.0f;
maximum_hp = 0.0f;
unknown_017 = 0;
unknown_018 = 0;
unknown_019 = 0;
current_mana = 0.0f;
maximum_mana = 0.0f;
unknown_020 = 0;
attack_power = 0;
minimum_damage = 0;
maximum_damage = 0;
unknown_021 = 0;
unknown_022 = 0;
unknown_023 = 0;
unknown_024 = 0;
unknown_025 = 0;
unknown_026 = 0;
current_gold = 0;
current_skill_points = 0;
current_ability_points = 0;
has_spent_remaining_points = 0;
unknown_029 = 0;
unknown_030 = 0;
unknown_031 = 0;
unknown_032 = 0;
unknown_033 = 0;
unknown_034 = 0;
unknown_035 = 0;
unknown_036 = 0;
unknown_037 = 0;
unknown_038 = 0;
unknown_039 = 0;
unknown_040 = 0;
weight = 0.0f;
max_weight = 0.0f;
unknown_041 = 0;
unknown_042 = 0;
item_data.fill( ItemData() );
num_armor_item = 0;
unknown_043 = 0;
armor_item_data.fill( ItemData() );
num_weapon_item = 0;
unknown_044 = 0;
weapon_item_data.fill( ItemData() );
num_consumable_item = 0;
unknown_045 = 0;
unknown_046.fill( 0 );
quest_string.fill( s_quest() );
num_quests = 0;
unknown_048 = 0;
unknown_049 = 0;
unknown_050 = 0;
unknown_051 = 0;
unknown_052 = 0;
unknown_053 = 0;
unknown_054 = 0;
equipment.fill( 0 );
newStyle.fill( s_new_style() );
unknown_075 = 0;
unknown_076 = 0.0f;
unknown_077 = 0;
unknown_078 = 0;
unknown_079 = 0;
unknown_080 = 0;
unknown_081 = 0;
unknown_082 = 0;
unknown_083 = 0;
unknown_084 = 0;
unknown_085 = 0;
skill_slot[ 0 ] = 0;
skill_slot[ 1 ] = 0;
unknown_087 = 0;
unknown_088 = 0;
attackData.fill( AttackData() );
skills.fill( s_skill() );
unknown_091 = 0;
mission_progress.fill( s_difficulty_progress() );
mission_medals.fill( 0 );
evil_bitflag = 0;
good_bitflag = 0;
unknown_101 = 0;
movement_speed = 0.0f;
unknown_102 = 0;
unknown_103 = 0;
unknown_104 = 0;
unknown_105 = 0;
unknown_106 = 0;
unknown_107 = 0;
unknown_108 = 0;
unknown_109 = 0;
unknown_110 = 0;
}
const std::vector< uint8_t > RealmCharacter::Serialize() const
{
auto writer = ByteBuffer( CHARACTER_DATA_SIZE );
/*writer.write_sz_utf8(name, 32);
for( const auto &val : unknown_000 )
{
writer.write_u8( val );
}
writer.write_sz_utf8( unknown_str, 32 );
for( const auto &val : unknown_004 )
{
writer.write_i32( val );
}
writer.write_i32( static_cast< int32_t >( character_class ) );
writer.write_i32( static_cast< int32_t >( character_race ) );
writer.write_u8( current_level );
writer.write_u8( pending_level );
writer.write_u16( unknown_009 );
writer.write_i32( experience );
for( const auto &val : unknown_010 )
{
writer.write_i32( val );
}
for( const auto &stat : stats )
{
writer.write( stat );
}
writer.write_i32( unknown_016 );
writer.write_f32( current_hp );
writer.write_f32( maximum_hp );
writer.write_i32( unknown_017 );
writer.write_i32( unknown_018 );
writer.write_i32( unknown_019 );
writer.write_f32( current_mana );
writer.write_f32( maximum_mana );
writer.write_i32( unknown_020 );
writer.write_i32( attack_power );
writer.write_i32( minimum_damage );
writer.write_i32( maximum_damage );
writer.write_i32( unknown_021 );
writer.write_i32( unknown_022 );
writer.write_u8( unknown_023 );
writer.write_u8( unknown_024 );
writer.write_u8( unknown_025 );
writer.write_u8( unknown_026 );
writer.write_i32( current_gold );
writer.write_i32( current_skill_points );
writer.write_i16( current_ability_points );
writer.write_i16( has_spent_remaining_points );
writer.write_i32( unknown_029 );
writer.write_i32( unknown_030 );
writer.write_i32( unknown_031 );
writer.write_i32( unknown_032 );
writer.write_i32( unknown_033 );
writer.write_i32( unknown_034 );
writer.write_i32( unknown_035 );
writer.write_i32( unknown_036 );
writer.write_i32( unknown_037 );
writer.write_i32( unknown_038 );
writer.write_i32( unknown_039 );
writer.write_i32( unknown_040 );
writer.write_f32( weight );
writer.write_f32( max_weight );
writer.write_i32( unknown_041 );
writer.write_i32( unknown_042 );
for( const auto &item : item_data )
{
writer.write( item );
}
writer.write_i32( num_armor_item );
writer.write_i32( unknown_043 );
for( const auto &item : armor_item_data )
{
writer.write( item );
}
writer.write_i32( num_weapon_item );
writer.write_i32( unknown_044 );
for( const auto &item : weapon_item_data )
{
writer.write( item );
}
writer.write_i32( num_consumable_item );
writer.write_i32( unknown_045 );
writer.forward( 2696 );
for( const auto &quest : quest_string )
{
writer.write_sz_utf8( quest.name, 32 );
writer.write_sz_utf8( quest.description, 32 );
}
writer.write_i32( num_quests );
writer.write_i32( unknown_048 );
writer.write_i32( unknown_049 );
writer.write_i32( unknown_050 );
writer.write_i32( unknown_051 );
writer.write_i32( unknown_052 );
writer.write_i32( unknown_053 );
writer.write_i32( unknown_054 );
for( const auto &slot : equipment )
{
writer.write_i32( slot );
}
for( const auto &style : newStyle )
{
writer.write< s_new_style >( style );
}
writer.write_i32( unknown_075 );
writer.write_f32( unknown_076 );
writer.write_i32( unknown_077 );
writer.write_i32( unknown_078 );
writer.write_i32( unknown_079 );
writer.write_i32( unknown_080 );
writer.write_i32( unknown_081 );
writer.write_i32( unknown_082 );
writer.write_i32( unknown_083 );
writer.write_i32( unknown_084 );
writer.write_i32( unknown_085 );
for( const auto &skill : skill_slot )
{
writer.write_u8( skill );
}
writer.write_u8( unknown_087 );
writer.write_u8( unknown_088 );
for( const auto &attack : attackData )
{
writer.write< AttackData >( attack );
}
for( const auto &skill : skills )
{
writer.write< s_skill >( skill );
}
writer.write_i32( unknown_091 );
for( const auto &progress : mission_progress )
{
writer.write< s_difficulty_progress >( progress );
}
for( const auto &medal : mission_medals )
{
writer.write_u8( medal );
}
writer.write_u8( evil_bitflag );
writer.write_u8( good_bitflag );
writer.write_i32( unknown_101 );
writer.write_f32( movement_speed );
writer.write_u8( unknown_102 );
writer.write_u8( unknown_103 );
writer.write_u8( unknown_104 );
writer.write_u8( unknown_105 );
writer.write_u8( unknown_106 );
writer.write_u8( unknown_107 );
writer.write_u8( unknown_108 );
writer.write_u8( unknown_109 );
writer.write_i32( unknown_110 );*/
return writer.get_buffer();
}
void RealmCharacter::Deserialize( const std::vector<uint8_t> &data )
{
ByteBufferReader reader( data );
/*try
{
name = reader.readString( 32 );
for( auto &val : unknown_000 )
{
val = reader.read< uint8_t >();
}
unknown_str = reader.readString( 32 );
for( auto &val : unknown_004 )
{
val = reader.read< int32_t >();
}
character_class = static_cast< CharacterClass >( reader.read< int32_t >() );
character_race = static_cast< CharacterRace >( reader.read< int32_t >() );
current_level = reader.read< uint8_t >();
pending_level = reader.read< uint8_t >();
unknown_009 = reader.read< uint16_t >();
experience = reader.read< int32_t >();
for( auto &val : unknown_010 )
{
val = reader.read< int32_t >();
}
for( auto &stat : stats )
{
stat = reader.read< s_stats >();
}
unknown_016 = reader.read< int32_t >();
current_hp = reader.read< float_t >();
maximum_hp = reader.read< float_t >();
unknown_017 = reader.read< int32_t >();
unknown_018 = reader.read< int32_t >();
unknown_019 = reader.read< int32_t >();
current_mana = reader.read< float_t >();
maximum_mana = reader.read< float_t >();
unknown_020 = reader.read< int32_t >();
attack_power = reader.read< int32_t >();
minimum_damage = reader.read< int32_t >();
maximum_damage = reader.read< int32_t >();
unknown_021 = reader.read< int32_t >();
unknown_022 = reader.read< int32_t >();
unknown_023 = reader.read< uint8_t >();
unknown_024 = reader.read< uint8_t >();
unknown_025 = reader.read< uint8_t >();
unknown_026 = reader.read< uint8_t >();
current_gold = reader.read< int32_t >();
current_skill_points = reader.read< int32_t >();
current_ability_points = reader.read< int16_t >();
has_spent_remaining_points = reader.read< int16_t >();
//
unknown_029 = reader.read< int32_t >();
unknown_030 = reader.read< int32_t >();
unknown_031 = reader.read< int32_t >();
unknown_032 = reader.read< int32_t >();
//
unknown_033 = reader.read< int32_t >();
unknown_034 = reader.read< int32_t >();
unknown_035 = reader.read< int32_t >();
unknown_036 = reader.read< int32_t >();
//
unknown_037 = reader.read< int32_t >();
unknown_038 = reader.read< int32_t >();
unknown_039 = reader.read< int32_t >();
unknown_040 = reader.read< int32_t >();
weight = reader.read< float_t >();
max_weight = reader.read< float_t >();
unknown_041 = reader.read< int32_t >();
unknown_042 = reader.read< int32_t >();
item_data = reader.readArray< ItemData, 64>();
num_armor_item = reader.read< int32_t >();
unknown_043 = reader.read< int32_t >();
armor_item_data = reader.readArray< ItemData, 64>();
num_weapon_item = reader.read< int32_t >();
unknown_044 = reader.read< int32_t >();
weapon_item_data = reader.readArray< ItemData, 64>();
num_consumable_item = reader.read< int32_t >();
unknown_045 = reader.read< int32_t >();
unknown_046.fill( 0x00 ); // Fill with zeros, size is 2696 bytes
for( auto &quest : quest_string )
{
quest = reader.read< s_quest >();
}
num_quests = reader.read< int32_t >();
unknown_048 = reader.read< int32_t >();
unknown_049 = reader.read< int32_t >();
unknown_050 = reader.read< int32_t >();
unknown_051 = reader.read< int32_t >();
unknown_052 = reader.read< int32_t >();
unknown_053 = reader.read< int32_t >();
unknown_054 = reader.read< int32_t >();
for( auto &slot : equipment )
{
slot = reader.read< int32_t >();
}
for( auto &style : newStyle )
{
style = reader.read< s_new_style >();
}
unknown_075 = reader.read< int32_t >();
unknown_076 = reader.read< float_t >();
unknown_077 = reader.read< int32_t >();
unknown_078 = reader.read< int32_t >();
unknown_079 = reader.read< int32_t >();
unknown_080 = reader.read< int32_t >();
unknown_081 = reader.read< int32_t >();
unknown_082 = reader.read< int32_t >();
unknown_083 = reader.read< int32_t >();
unknown_084 = reader.read< int32_t >();
unknown_085 = reader.read< int32_t >();
for( auto &skill : skill_slot )
{
skill = reader.read< uint8_t >();
}
unknown_087 = reader.read< uint8_t >();
unknown_088 = reader.read< uint8_t >();
for( auto &attack : attackData )
{
attack = reader.read< AttackData >();
}
for( auto &skill : skills )
{
skill.skill_id = reader.read< int16_t >();
skill.skill_level = reader.read< int16_t >();
}
unknown_091 = reader.read< int32_t >();
for( auto &val : mission_progress )
{
val = reader.read< s_difficulty_progress >();
}
for( auto &val : mission_medals )
{
val = reader.read< uint8_t >();
}
evil_bitflag = reader.read< uint8_t >();
good_bitflag = reader.read< uint8_t >();
unknown_101 = reader.read< int32_t >();
movement_speed = reader.read< float_t >();
unknown_102 = reader.read< uint8_t >();
unknown_103 = reader.read< uint8_t >();
unknown_104 = reader.read< uint8_t >();
unknown_105 = reader.read< uint8_t >();
unknown_106 = reader.read< uint8_t >();
unknown_107 = reader.read< uint8_t >();
unknown_108 = reader.read< uint8_t >();
unknown_109 = reader.read< uint8_t >();
unknown_110 = reader.read< int32_t >();
}
catch( const std::out_of_range &e )
{
Log::Error( "Failed to deserialize RealmCharacter: %s", e.what() );
return;
}*/
}
bool RealmCharacter::ValidateData()
{
if( m_data.empty() || m_data.size() < CHARACTER_DATA_SIZE )
{
Log::Error( "Character data is invalid or too small!" );
return false;
}
if( current_level < 1 || current_level > 80 )
{
Log::Error( "Invalid character level: %d", current_level );
return false;
}
if( current_hp < 1.0f || current_hp > maximum_hp )
{
Log::Error( "Invalid HP values: current %f, maximum %f", current_hp, maximum_hp );
return false;
}
if( current_mana < 0.0f || current_mana > maximum_mana )
{
Log::Error( "Invalid mana values: current %f, maximum %f", current_mana, maximum_mana );
return false;
}
if( weight < 0.0f || weight > max_weight )
{
Log::Error( "Invalid weight values: current %f, maximum %f", weight, max_weight );
return false;
}
if( movement_speed <= 0.0f )
{
Log::Error( "Invalid movement speed: %f", movement_speed );
return false;
}
for( const auto &mission : mission_progress )
{
if( mission.mission_na < 0 || mission.mission_na > 3 )
{
Log::Error( "Invalid mission NA value: %d", mission.mission_na );
return false;
}
if( mission.mission_Disease < 0 || mission.mission_Disease > 3 )
{
Log::Error( "Invalid mission Disease value: %d", mission.mission_Disease );
return false;
}
if( mission.mission_Fear < 0 || mission.mission_Fear > 3 )
{
Log::Error( "Invalid mission Fear value: %d", mission.mission_Fear );
return false;
}
}
return true;
}
CharacterSlotData RealmCharacter::GetMetaData() const
{
return m_metaData;
}
void RealmCharacter::SetMetaData( sptr_byte_stream stream )
{
m_metaData.Deserialize( stream );
}
std::vector<uint8_t> RealmCharacter::Unpack()
{
std::vector< uint8_t > output;
size_t read = 4;
while( read < m_data.size() )
{
uint8_t byte = m_data[ read++ ];
output.push_back( byte );
if( byte == 0x00 )
{
if( read >= m_data.size() )
{
break;
}
uint8_t count = m_data[ read++ ];
output.insert( output.end(), count, 0x00 );
}
}
return output;
}

View File

@@ -0,0 +1,93 @@
#include "Game/RealmCharacterMetaKV.hpp"
#include "Common/ByteBufferReader.hpp"
#include "Common/ByteStream.hpp"
#include "Common/Utility.hpp"
#include "logging.hpp"
CharacterSlotData::CharacterSlotData(const std::vector<uint8_t>& data)
{
if( !data.empty() )
{
Deserialize(data);
}
else
{
m_metaData.clear();
}
}
void CharacterSlotData::Deserialize(const std::vector< uint8_t >& data)
{
ByteBuffer reader(data);
auto numberOfKeys = reader.read_i32();
if( !Util::IsInRange( numberOfKeys, 0, 4 ) )
{
m_metaData.clear();
return;
}
m_metaData.clear();
m_metaData.reserve( numberOfKeys );
for( auto i = 0; i < numberOfKeys; ++i )
{
std::wstring key = reader.read_utf16();
m_metaData.emplace_back( key, L"" );
}
auto numberOfValues = reader.read_i32();
for( auto &pair : m_metaData )
{
pair.second = reader.read_utf16();
}
}
void CharacterSlotData::Deserialize(const sptr_byte_stream stream)
{
auto numberOfKeys = stream->read_i32();
if (!Util::IsInRange(numberOfKeys, 0, 4))
{
m_metaData.clear();
return;
}
m_metaData.clear();
m_metaData.reserve(numberOfKeys);
for (auto i = 0; i < numberOfKeys; ++i)
{
std::wstring key = stream->read_utf16();
m_metaData.emplace_back(key, L"");
}
auto numberOfValues = stream->read_i32();
for (auto& pair : m_metaData)
{
pair.second = stream->read_utf16();
}
}
std::vector< uint8_t > CharacterSlotData::Serialize() const
{
ByteBuffer stream;
// Number of keys
stream.write_u32( static_cast< int32_t >( m_metaData.size() ) );
for( const auto &pair : m_metaData )
{
stream.write_utf16( pair.first );
}
// Number of values
stream.write_u32( static_cast< uint32_t >( m_metaData.size() ) );
for( const auto &pair : m_metaData )
{
stream.write_utf16( pair.second );
}
return stream.m_buffer;
}

View File

@@ -0,0 +1,105 @@
#include "Game/CharacterSaveTask.hpp"
#include "Game/RealmCharacter.hpp"
#include "Common/ByteBufferReader.hpp"
#include "logging.hpp"
CharacterSaveTask::CharacterSaveTask( CharacterSaveType Type, uint32_t characterId )
{
m_saveType = Type;
m_ownerUser.reset();
m_targetUser.reset();
m_characterId = characterId;
m_writePosition = 0;
m_data.reserve(4096);
}
CharacterSaveTask::~CharacterSaveTask()
{
m_ownerUser.reset();
m_targetUser.reset();
m_data.clear();
}
void CharacterSaveTask::SetMetaData( const CharacterSlotData &metaData )
{
this->m_meta = metaData;
}
bool CharacterSaveTask::AppendData( const std::vector< uint8_t > &data )
{
if( data.empty() )
{
Log::Error( "AppendTailData called with empty data!" );
return false;
}
m_data.insert( m_data.end(), data.begin(), data.end() );
return true;
}
bool CharacterSaveTask::Validate()
{
if( m_data.empty() )
{
Log::Error( "EndUpdate called with empty pending data!" );
return false;
}
if( m_meta.empty() )
{
Log::Error( "EndUpdate called with empty metadata!" );
return false;
}
// Verify the size of the decompressed buffer
size_t read = 4;
size_t decompressedSize = 0;
size_t lastUsedByte = read;
while( read < m_data.size() )
{
uint8_t byte = m_data[ read++ ];
decompressedSize += 1;
lastUsedByte = read;
if( byte == 0x00 )
{
if( read >= m_data.size() )
{
Log::Error( "Unexpected end of data during decompression" );
return false;
}
uint8_t count = m_data[ read++ ];
decompressedSize += count;
lastUsedByte = read;
}
if( decompressedSize >= CHARACTER_DATA_SIZE )
{
break;
}
}
if( decompressedSize < CHARACTER_DATA_SIZE )
{
Log::Error( "Failed to decompress pending character data. More data needed!" );
return false;
}
// TODO: Better error checking.
// Trim Garbage from the compressed buffer.
m_data.resize( lastUsedByte );
return true;
}
const std::vector<uint8_t> &CharacterSaveTask::GetData() const
{
return m_data;
}

50
Source/Game/RealmUser.cpp Normal file
View File

@@ -0,0 +1,50 @@
#include "Game/RealmUser.hpp"
#include "Game/RealmCharacter.hpp"
RealmUser::RealmUser()
{
sock = nullptr;
m_gameType = RealmGameType::CHAMPIONS_OF_NORRATH;
m_accountId = -1;
m_sessionId = L"";
m_username = L"";
m_characterId = 0;
m_localAddr = "";
m_discoveryAddr = "";
m_discoveryPort = 0;
m_isLoggedIn = false;
m_isHost = false;
m_gameId = -1;
m_publicRoomId = -1;
m_privateRoomId = -1;
}
RealmUser::~RealmUser()
{
if( sock )
{
sock->flag.disconnected_wait = true;
sock.reset();
}
m_gameType = RealmGameType::CHAMPIONS_OF_NORRATH;
m_accountId = 0;
m_sessionId = L"";
m_username = L"";
m_characterId = 0;
m_localAddr = "";
m_discoveryAddr = "";
m_discoveryPort = 0;
m_isLoggedIn = false;
m_isHost = false;
m_gameId = 0;
m_publicRoomId = -1;
m_privateRoomId = -1;
}

View File

@@ -0,0 +1,180 @@
#include "Game/RealmUserManager.hpp"
#include "Game/GameSessionManager.hpp"
#include "Game/ChatRoomManager.hpp"
#include "Network/Event/NotifyForcedLogout.hpp"
#include "Network/Event/NotifyFriendStatus.hpp"
#include "Database/Database.hpp"
#include "Common/Constant.hpp"
#include "logging.hpp"
UserManager::UserManager()
{
std::random_device rd;
rng.seed( rd() );
m_users.clear();
}
UserManager::~UserManager()
{
}
std::wstring UserManager::GenerateSessionId()
{
static const wchar_t charset[] = L"0123456789ABCDEF";
std::uniform_int_distribution<int> dist( 0, 15 );
std::wstring sessionId;
sessionId.reserve( MAX_SESSION_ID_LENGTH );
for( int i = 0; i < MAX_SESSION_ID_LENGTH; ++i )
{
sessionId += charset[ dist( rng ) ];
}
return sessionId;
}
sptr_user UserManager::CreateUser( sptr_socket socket, RealmGameType clientType )
{
Log::Debug( "ClientManager::CreateUser() - Created new user" );
auto user = std::make_shared< RealmUser >();
user->sock = socket;
user->m_gameType = clientType;
std::lock_guard< std::mutex > lock( m_mutex );
m_users.push_back( user );
return user;
}
void UserManager::RemoveUser( sptr_user user )
{
auto it = std::find( m_users.begin(), m_users.end(), user );
if( it == m_users.end() )
{
Log::Error( "RemoveUser : [{}] not found", user->m_sessionId );
return;
}
GameSessionManager::Get().OnDisconnectUser( user );
ChatRoomManager::Get().OnDisconnectUser( user );
NotifyFriendsOnlineStatus( user, false );
Log::Debug( "RemoveUser : [{}][{}]", user->m_username, user->m_sessionId );
std::lock_guard< std::mutex > lock( m_mutex );
m_users.erase( it );
}
void UserManager::RemoveUser( const std::wstring &sessionId )
{
if( auto user = FindUserBySessionId( sessionId ) )
{
RemoveUser( user );
}
}
void UserManager::RemoveUser( const sptr_socket socket )
{
if( auto user = FindUserBySocket( socket ) )
{
RemoveUser( user );
}
}
void UserManager::Disconnect( sptr_socket socket, const std::string reason )
{
if( nullptr == socket )
{
return;
}
Log::Debug( "DisconnectSocket : [{}]. Reason: {}", socket->remote_ip, reason );
socket->send( NotifyForcedLogout() );
socket->flag.disconnected_wait = true;
RemoveUser( socket );
}
void UserManager::Disconnect( sptr_user user, const std::string reason )
{
if( nullptr == user )
{
return;
}
if( user->sock != nullptr )
{
user->sock->send( NotifyForcedLogout() );
user->sock->flag.disconnected_wait = true;
}
Log::Debug( "DisconnectUser : [{}]. Reason: {}", user->m_sessionId, reason );
RemoveUser( user );
}
sptr_user UserManager::FindUserBySessionId( const std::wstring &sessionId )
{
std::lock_guard<std::mutex> lock( m_mutex );
auto it = std::find_if( m_users.begin(), m_users.end(), [ & ]( const sptr_user &user )
{
return user->m_sessionId == sessionId;
} );
return ( it != m_users.end() ) ? *it : nullptr;
}
sptr_user UserManager::FindUserBySocket( const sptr_socket &socket )
{
std::lock_guard<std::mutex> lock( m_mutex );
auto it = std::find_if( m_users.begin(), m_users.end(), [ & ]( const sptr_user &user )
{
return user->sock == socket;
} );
return ( it != m_users.end() ) ? *it : nullptr;
}
sptr_user UserManager::FindUserByChatHandle( const std::wstring &handle )
{
std::lock_guard<std::mutex> lock( m_mutex );
auto it = std::find_if( m_users.begin(), m_users.end(), [ & ]( const sptr_user &user )
{
return user->m_chatHandle == handle;
} );
return ( it != m_users.end() ) ? *it : nullptr;
}
int32_t UserManager::GetUserCount() const
{
return static_cast< int32_t >( m_users.size() );
}
std::vector<sptr_user> UserManager::GetUserList()
{
std::lock_guard<std::mutex> lock( m_mutex );
return m_users;
}
void UserManager::NotifyFriendsOnlineStatus( const sptr_user &user, bool onlineStatus )
{
if( !user || user->m_friendList.empty() )
{
return;
}
const auto notifyFriend = NotifyFriendStatus( user->m_chatHandle, onlineStatus );
for( const auto &friendHandle : user->m_friendList )
{
auto friendUser = FindUserByChatHandle( friendHandle );
if( friendUser && friendUser->sock )
{
friendUser->sock->send( notifyFriend );
}
}
}