mirror of
https://github.com/HikikoMarmy/Champions-Reborn-Server.git
synced 2026-04-05 00:49:48 -03:00
Reorganized and cleaned up the solution.
This commit is contained in:
203
Source/Game/CharacterSaveManager.cpp
Normal file
203
Source/Game/CharacterSaveManager.cpp
Normal 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
76
Source/Game/ChatRoom.cpp
Normal 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;
|
||||
}
|
||||
294
Source/Game/ChatRoomManager.cpp
Normal file
294
Source/Game/ChatRoomManager.cpp
Normal 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
186
Source/Game/GameSession.cpp
Normal 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;
|
||||
}
|
||||
422
Source/Game/GameSessionManager.cpp
Normal file
422
Source/Game/GameSessionManager.cpp
Normal 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 ) );
|
||||
}
|
||||
659
Source/Game/RealmCharacter.cpp
Normal file
659
Source/Game/RealmCharacter.cpp
Normal 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;
|
||||
}
|
||||
93
Source/Game/RealmCharacterMetaKV.cpp
Normal file
93
Source/Game/RealmCharacterMetaKV.cpp
Normal 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;
|
||||
}
|
||||
105
Source/Game/RealmCharacterSaveTask.cpp
Normal file
105
Source/Game/RealmCharacterSaveTask.cpp
Normal 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
50
Source/Game/RealmUser.cpp
Normal 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;
|
||||
}
|
||||
180
Source/Game/RealmUserManager.cpp
Normal file
180
Source/Game/RealmUserManager.cpp
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user