mirror of
https://github.com/HikikoMarmy/Champions-Reborn-Server.git
synced 2026-04-05 08:59:54 -03:00
Network character saving
This commit is contained in:
203
Game/CharacterSaveManager.cpp
Normal file
203
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 "CharacterSaveManager.h"
|
||||
|
||||
#include "RealmUser.h"
|
||||
#include "RealmUserManager.h"
|
||||
#include "../Database/Database.h"
|
||||
#include "../../logging.h"
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
43
Game/CharacterSaveManager.h
Normal file
43
Game/CharacterSaveManager.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "CharacterSaveTask.h"
|
||||
|
||||
class CharacterSaveManager {
|
||||
public:
|
||||
CharacterSaveManager();
|
||||
~CharacterSaveManager();
|
||||
CharacterSaveManager( const CharacterSaveManager & ) = delete;
|
||||
CharacterSaveManager &operator=( const CharacterSaveManager & ) = delete;
|
||||
|
||||
static CharacterSaveManager &Get()
|
||||
{
|
||||
static CharacterSaveManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool BeginSaveTask(
|
||||
const sptr_user user,
|
||||
const uint32_t characterId,
|
||||
const CharacterSlotData &metaData,
|
||||
const CharacterSaveType saveType );
|
||||
|
||||
bool BeginSaveTask(
|
||||
const sptr_user m_owner,
|
||||
const sptr_user m_target,
|
||||
const uint32_t characterId,
|
||||
const CharacterSlotData &metaData,
|
||||
const CharacterSaveType saveType );
|
||||
|
||||
void AppendSaveData( const std::wstring &sessionId, const std::vector<uint8_t> &data, bool endOfData );
|
||||
bool CommitSaveTask( const std::wstring &sessionId );
|
||||
void RemoveSaveTask( const std::wstring &sessionId );
|
||||
|
||||
sptr_character_save_task FindSaveTask( const std::wstring &sessionId );
|
||||
|
||||
public:
|
||||
std::unordered_map< std::wstring, sptr_character_save_task > m_tasks;
|
||||
};
|
||||
43
Game/CharacterSaveTask.h
Normal file
43
Game/CharacterSaveTask.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../Common/Constant.h"
|
||||
#include "../Common/ByteStream.h"
|
||||
#include "RealmUser.h"
|
||||
#include "RealmCharacterMetaKV.h"
|
||||
|
||||
enum class CharacterSaveType : uint8_t
|
||||
{
|
||||
NEW_CHARACTER,
|
||||
SAVE_CHARACTER
|
||||
};
|
||||
|
||||
class CharacterSaveTask {
|
||||
public:
|
||||
CharacterSaveTask( CharacterSaveType Type, uint32_t characterId = 0 );
|
||||
~CharacterSaveTask();
|
||||
|
||||
void SetMetaData( const CharacterSlotData &metaData );
|
||||
bool AppendData( const std::vector< uint8_t > &data );
|
||||
|
||||
bool Validate();
|
||||
|
||||
const std::vector< uint8_t >& GetData() const;
|
||||
|
||||
public:
|
||||
CharacterSaveType m_saveType;
|
||||
sptr_user m_ownerUser;
|
||||
sptr_user m_targetUser;
|
||||
|
||||
uint32_t m_characterId;
|
||||
uint32_t m_writePosition;
|
||||
|
||||
CharacterSlotData m_meta;
|
||||
std::vector< uint8_t > m_data;
|
||||
};
|
||||
|
||||
using sptr_character_save_task = std::shared_ptr< CharacterSaveTask >;
|
||||
@@ -1,145 +1,660 @@
|
||||
#include "RealmCharacter.h"
|
||||
|
||||
#include "CharacterSaveTask.h"
|
||||
#include "../Common/ByteBufferReader.hpp"
|
||||
#include "../Common/RLEZ.hpp"
|
||||
|
||||
#include "../../logging.h"
|
||||
|
||||
RealmCharacter::RealmCharacter( std::vector< uint8_t > &data )
|
||||
RealmCharacter::RealmCharacter()
|
||||
{
|
||||
ByteBuffer stream( data );
|
||||
m_characterId = 0;
|
||||
m_data.clear();
|
||||
|
||||
m_metaData.Deserialize( stream );
|
||||
Initialize();
|
||||
}
|
||||
|
||||
auto characterDataSize = stream.read_u32();
|
||||
if( characterDataSize > 0 && characterDataSize < 1024 )
|
||||
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 )
|
||||
{
|
||||
m_characterData = stream.read_bytes( characterDataSize );
|
||||
val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
RealmCharacter::RealmCharacter(sptr_byte_stream stream)
|
||||
{
|
||||
m_metaData.Deserialize(*stream);
|
||||
unknown_str = "";
|
||||
|
||||
auto characterDataSize = stream->read_u32();
|
||||
if (characterDataSize > 0 && characterDataSize < 1024)
|
||||
for( auto &val : unknown_004 )
|
||||
{
|
||||
m_characterData = stream->read_bytes(characterDataSize);
|
||||
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::GetCharacterData() const
|
||||
const std::vector< uint8_t > RealmCharacter::Serialize() const
|
||||
{
|
||||
return m_characterData;
|
||||
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::SetCharacterData( const std::vector<uint8_t> &data )
|
||||
void RealmCharacter::Deserialize( const std::vector<uint8_t> &data )
|
||||
{
|
||||
m_characterData = 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;
|
||||
}*/
|
||||
}
|
||||
|
||||
const std::vector< uint8_t > RealmCharacter::GetInventoryData() const
|
||||
bool RealmCharacter::ValidateData()
|
||||
{
|
||||
return m_inventoryData;
|
||||
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;
|
||||
}
|
||||
|
||||
void RealmCharacter::SetInventoryData( const std::vector<uint8_t> &data )
|
||||
{
|
||||
m_inventoryData = data;
|
||||
}
|
||||
|
||||
RealmCharacterMetaData RealmCharacter::GetMetaData() const
|
||||
CharacterSlotData RealmCharacter::GetMetaData() const
|
||||
{
|
||||
return m_metaData;
|
||||
}
|
||||
|
||||
void RealmCharacter::SetMetaData( const std::vector<uint8_t> &data )
|
||||
void RealmCharacter::SetMetaData( sptr_byte_stream stream )
|
||||
{
|
||||
ByteBuffer stream( data );
|
||||
m_metaData.Deserialize( stream );
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> RealmCharacter::Unpack()
|
||||
std::vector<uint8_t> RealmCharacter::Unpack()
|
||||
{
|
||||
std::vector<uint8_t> output;
|
||||
const int expectedOutputSize = 19504;
|
||||
const size_t inputSize = m_characterData.size();
|
||||
const uint8_t* input = m_characterData.data();
|
||||
output.reserve(expectedOutputSize);
|
||||
std::vector< uint8_t > output;
|
||||
size_t read = 4;
|
||||
|
||||
try
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < inputSize && output.size() < expectedOutputSize) {
|
||||
uint8_t byte = input[i++];
|
||||
if (byte != 0x00) {
|
||||
output.push_back(byte);
|
||||
}
|
||||
else {
|
||||
if (i >= inputSize) {
|
||||
throw std::runtime_error("Invalid blob format: zero-byte with no length");
|
||||
}
|
||||
uint8_t next = input[i++];
|
||||
if (next == 0x00) {
|
||||
// Escaped literal 0x00 byte
|
||||
output.push_back(0x00);
|
||||
}
|
||||
else {
|
||||
// Insert `next` zero bytes
|
||||
output.insert(output.end(), next, 0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
while( read < m_data.size() )
|
||||
{
|
||||
Log::Error("Error unpacking character data: " + std::string(e.what()));
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we have exactly 19504 bytes
|
||||
if (output.size() != expectedOutputSize)
|
||||
{
|
||||
Log::Error("Unpacked character data size mismatch: expected " + std::to_string(expectedOutputSize) + ", got " + std::to_string(output.size()));
|
||||
}
|
||||
|
||||
return output;
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RealmCharacter::Pack(const std::vector<uint8_t>& input)
|
||||
{
|
||||
constexpr size_t ExpectedInputSize = 19504;
|
||||
if (input.size() != ExpectedInputSize)
|
||||
{
|
||||
throw std::runtime_error("Input to compressor must be exactly 19504 bytes");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> output;
|
||||
|
||||
// Optional: add 4-byte header if game expects it (match decompress behavior)
|
||||
output.insert(output.end(), { 0x00, 0x00, 0x00, 0x00 }); // Placeholder header
|
||||
|
||||
size_t i = 0;
|
||||
while (i < input.size())
|
||||
{
|
||||
uint8_t byte = input[i];
|
||||
if (byte != 0x00)
|
||||
{
|
||||
// Literal non-zero byte, just write it
|
||||
output.push_back(byte);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Zero run: count how many consecutive 0x00s (max 255)
|
||||
size_t zeroCount = 0;
|
||||
while (i < input.size() && input[i] == 0x00 && zeroCount < 255)
|
||||
{
|
||||
++zeroCount;
|
||||
++i;
|
||||
}
|
||||
|
||||
output.push_back(0x00); // Zero marker
|
||||
output.push_back(static_cast<uint8_t>(zeroCount)); // Count
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -1,40 +1,170 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../Common/Constant.h"
|
||||
#include "../Common/ByteStream.h"
|
||||
#include "RealmCharacterMetaKV.h"
|
||||
|
||||
constexpr size_t MAX_NUMBER_OF_CHARACTERS = 12;
|
||||
constexpr size_t CHARACTER_DATA_SIZE = 19504;
|
||||
|
||||
using ItemData = std::array< uint8_t, 72 >;
|
||||
using AttackData = std::array< float_t, 4 >;
|
||||
|
||||
class RealmCharacter {
|
||||
public:
|
||||
// Character metadata, such as name, class, etc.
|
||||
// This is used for displaying character information in the character selection screen.
|
||||
RealmCharacterMetaData m_metaData;
|
||||
RealmCharacter();
|
||||
RealmCharacter( const int32_t id, const CharacterSlotData meta, const std::vector< uint8_t > data );
|
||||
~RealmCharacter();
|
||||
|
||||
// Character data blob, such as stats, skills, etc.
|
||||
std::vector< uint8_t > m_characterData;
|
||||
void Initialize();
|
||||
const std::vector< uint8_t > Serialize() const;
|
||||
void Deserialize( const std::vector< uint8_t > &data );
|
||||
|
||||
// Inventory data blob
|
||||
std::vector< uint8_t > m_inventoryData;
|
||||
bool ValidateData();
|
||||
|
||||
CharacterSlotData GetMetaData() const;
|
||||
void SetMetaData( sptr_byte_stream stream );
|
||||
|
||||
std::vector< uint8_t > Unpack();
|
||||
|
||||
public:
|
||||
RealmCharacter() = default;
|
||||
RealmCharacter( std::vector< uint8_t > &data );
|
||||
RealmCharacter(sptr_byte_stream stream);
|
||||
uint32_t m_characterId;
|
||||
CharacterSlotData m_metaData;
|
||||
std::vector< uint8_t > m_data;
|
||||
|
||||
const std::vector< uint8_t > GetCharacterData() const;
|
||||
void SetCharacterData( const std::vector< uint8_t > &data );
|
||||
public:
|
||||
std::string name;
|
||||
uint8_t unknown_000[ 4 ];
|
||||
std::string unknown_str;
|
||||
int32_t unknown_004[ 3 ];
|
||||
|
||||
const std::vector< uint8_t > GetInventoryData() const;
|
||||
void SetInventoryData( const std::vector< uint8_t > &data );
|
||||
CharacterClass character_class;
|
||||
CharacterRace character_race;
|
||||
|
||||
RealmCharacterMetaData GetMetaData() const;
|
||||
void SetMetaData( const std::vector< uint8_t > &data );
|
||||
uint8_t current_level;
|
||||
uint8_t pending_level; // Waiting to spend points, basically.
|
||||
uint16_t unknown_009;
|
||||
int32_t experience;
|
||||
|
||||
const std::vector< uint8_t > Unpack();
|
||||
std::vector< uint8_t > Pack(const std::vector<uint8_t>& input);
|
||||
int32_t unknown_010[ 6 ];
|
||||
|
||||
struct s_stats {
|
||||
int32_t strength, intelligence, dexterity, stamina, unknown_a, unknown_b;
|
||||
} stats[ 2 ];
|
||||
|
||||
int32_t unknown_016;
|
||||
float_t current_hp, maximum_hp;
|
||||
int32_t unknown_017, unknown_018, unknown_019;
|
||||
float_t current_mana, maximum_mana;
|
||||
int32_t unknown_020;
|
||||
int32_t attack_power, minimum_damage, maximum_damage;
|
||||
int32_t unknown_021, unknown_022;
|
||||
|
||||
uint8_t unknown_023, unknown_024, unknown_025, unknown_026;
|
||||
int32_t current_gold, current_skill_points;
|
||||
int16_t current_ability_points;
|
||||
int16_t has_spent_remaining_points;
|
||||
|
||||
int32_t unknown_029, unknown_030, unknown_031, unknown_032;
|
||||
int32_t unknown_033, unknown_034, unknown_035, unknown_036;
|
||||
int32_t unknown_037, unknown_038, unknown_039, unknown_040;
|
||||
|
||||
float_t weight, max_weight;
|
||||
int32_t unknown_041, unknown_042;
|
||||
|
||||
std::array<ItemData, 64> item_data;
|
||||
int32_t num_armor_item, unknown_043;
|
||||
std::array<ItemData, 64> armor_item_data;
|
||||
|
||||
int32_t num_weapon_item, unknown_044;
|
||||
std::array<ItemData, 64> weapon_item_data;
|
||||
|
||||
int32_t num_consumable_item, unknown_045;
|
||||
std::array< uint8_t, 2696 > unknown_046;
|
||||
|
||||
struct s_quest {
|
||||
char name[ 32 ];
|
||||
char description[ 32 ];
|
||||
};
|
||||
|
||||
std::array<s_quest, 8> quest_string;
|
||||
|
||||
int32_t num_quests;
|
||||
|
||||
int32_t unknown_048;
|
||||
int32_t unknown_049;
|
||||
int32_t unknown_050;
|
||||
int32_t unknown_051;
|
||||
int32_t unknown_052;
|
||||
int32_t unknown_053;
|
||||
int32_t unknown_054;
|
||||
|
||||
std::array< int32_t, 20 > equipment;
|
||||
|
||||
struct s_new_style {
|
||||
char name[ 32 ]; // "newstyle"
|
||||
int id;
|
||||
uint8_t active_flag;
|
||||
uint8_t type;
|
||||
uint8_t unknown_a;
|
||||
uint8_t unknown_b;
|
||||
uint8_t reserved[ 64 ];
|
||||
};
|
||||
|
||||
std::array<s_new_style, 15> newStyle;
|
||||
|
||||
int32_t unknown_075;
|
||||
float_t unknown_076;
|
||||
|
||||
int32_t
|
||||
unknown_077, unknown_078, unknown_079,
|
||||
unknown_080, unknown_081, unknown_082,
|
||||
unknown_083, unknown_084, unknown_085;
|
||||
|
||||
uint8_t skill_slot[ 2 ];
|
||||
uint8_t unknown_087, unknown_088;
|
||||
|
||||
std::array< AttackData, 8> attackData;
|
||||
|
||||
struct s_skill {
|
||||
int16_t skill_id;
|
||||
int16_t skill_level;
|
||||
};
|
||||
std::array<s_skill, 48> skills;
|
||||
|
||||
int32_t unknown_091;
|
||||
|
||||
struct s_difficulty_progress {
|
||||
uint8_t mission_na;
|
||||
uint8_t mission_War;
|
||||
uint8_t mission_Innovation;
|
||||
uint8_t mission_PitOfIllOmen;
|
||||
uint8_t mission_PlaneOfWater;
|
||||
uint8_t mission_Torment;
|
||||
uint8_t mission_Disease;
|
||||
uint8_t mission_Valor;
|
||||
uint8_t mission_Fire;
|
||||
uint8_t mission_Storms;
|
||||
uint8_t mission_Faydark;
|
||||
uint8_t mission_Nightmares;
|
||||
uint8_t mission_Fear;
|
||||
};
|
||||
|
||||
std::array<s_difficulty_progress, 5> mission_progress;
|
||||
std::array< uint8_t, 13 > mission_medals;
|
||||
|
||||
uint8_t evil_bitflag, good_bitflag;
|
||||
int32_t unknown_101;
|
||||
float_t movement_speed;
|
||||
|
||||
uint8_t unknown_102, unknown_103, unknown_104, unknown_105;
|
||||
uint8_t unknown_106, unknown_107, unknown_108, unknown_109;
|
||||
int32_t unknown_110;
|
||||
};
|
||||
|
||||
using sptr_realm_character = std::shared_ptr< RealmCharacter >;
|
||||
@@ -1,20 +1,28 @@
|
||||
#include "RealmCharacterMetaKV.h"
|
||||
|
||||
#include "../Common/ByteBufferReader.hpp"
|
||||
#include "../Common/ByteStream.h"
|
||||
#include "../Common/Utility.h"
|
||||
#include "../logging.h"
|
||||
|
||||
RealmCharacterMetaData::RealmCharacterMetaData( const std::vector<uint8_t> &data )
|
||||
CharacterSlotData::CharacterSlotData(const std::vector<uint8_t>& data)
|
||||
{
|
||||
ByteBuffer stream( data );
|
||||
Deserialize( stream );
|
||||
if( !data.empty() )
|
||||
{
|
||||
Deserialize(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_metaData.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCharacterMetaData::Deserialize( ByteBuffer &stream )
|
||||
void CharacterSlotData::Deserialize(const std::vector< uint8_t >& data)
|
||||
{
|
||||
auto numberOfKeys = stream.read_u32();
|
||||
ByteBuffer reader(data);
|
||||
|
||||
if( !Util::IsInRange< int32_t >( numberOfKeys, 0, 4 ) )
|
||||
auto numberOfKeys = reader.read_i32();
|
||||
if( !Util::IsInRange( numberOfKeys, 0, 4 ) )
|
||||
{
|
||||
m_metaData.clear();
|
||||
return;
|
||||
@@ -25,25 +33,43 @@ void RealmCharacterMetaData::Deserialize( ByteBuffer &stream )
|
||||
|
||||
for( auto i = 0; i < numberOfKeys; ++i )
|
||||
{
|
||||
std::wstring key = stream.read_utf16();
|
||||
std::wstring key = reader.read_utf16();
|
||||
m_metaData.emplace_back( key, L"" );
|
||||
}
|
||||
|
||||
auto numberOfValues = stream.read_u32();
|
||||
|
||||
auto numberOfValues = reader.read_i32();
|
||||
for( auto &pair : m_metaData )
|
||||
{
|
||||
pair.second = stream.read_utf16();
|
||||
pair.second = reader.read_utf16();
|
||||
}
|
||||
}
|
||||
|
||||
void RealmCharacterMetaData::Deserialize( const std::vector<uint8_t> &data )
|
||||
void CharacterSlotData::Deserialize(const sptr_byte_stream stream)
|
||||
{
|
||||
ByteBuffer stream( data );
|
||||
Deserialize( 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 > RealmCharacterMetaData::Serialize() const
|
||||
std::vector< uint8_t > CharacterSlotData::Serialize() const
|
||||
{
|
||||
ByteBuffer stream;
|
||||
|
||||
|
||||
@@ -6,32 +6,37 @@
|
||||
|
||||
#include "../Common/ByteStream.h"
|
||||
|
||||
using CharacterMetaKV = std::pair<std::wstring, std::wstring>;
|
||||
using CharacterAttributeKV = std::pair<std::wstring, std::wstring>;
|
||||
|
||||
class RealmCharacterMetaData {
|
||||
class CharacterSlotData {
|
||||
private:
|
||||
std::vector<CharacterMetaKV> m_metaData;
|
||||
std::vector<CharacterAttributeKV> m_metaData;
|
||||
|
||||
public:
|
||||
RealmCharacterMetaData() = default;
|
||||
explicit RealmCharacterMetaData( const std::vector<uint8_t> &data );
|
||||
CharacterSlotData() = default;
|
||||
CharacterSlotData( const std::vector< uint8_t > &data );
|
||||
|
||||
void Deserialize( ByteBuffer &stream );
|
||||
void Deserialize( const std::vector<uint8_t> &data );
|
||||
void Deserialize( const std::vector< uint8_t > &data );
|
||||
void Deserialize( const sptr_byte_stream stream );
|
||||
|
||||
std::vector<uint8_t> Serialize() const;
|
||||
std::vector<uint8_t> Serialize() const;
|
||||
|
||||
const std::vector<CharacterMetaKV> &GetMetaData() const
|
||||
{
|
||||
return m_metaData;
|
||||
}
|
||||
bool empty() const
|
||||
{
|
||||
return m_metaData.empty();
|
||||
}
|
||||
|
||||
void SetMetaData( const std::vector<CharacterMetaKV> &metaData )
|
||||
{
|
||||
m_metaData = metaData;
|
||||
}
|
||||
const std::vector<CharacterAttributeKV> &GetMetaData() const
|
||||
{
|
||||
return m_metaData;
|
||||
}
|
||||
|
||||
std::wstring GetValue( std::wstring key )
|
||||
void SetMetaData( const std::vector<CharacterAttributeKV> &metaData )
|
||||
{
|
||||
m_metaData = metaData;
|
||||
}
|
||||
|
||||
std::wstring GetValue( std::wstring key )
|
||||
{
|
||||
for( const auto &kv : m_metaData )
|
||||
{
|
||||
|
||||
105
Game/RealmCharacterSaveTask.cpp
Normal file
105
Game/RealmCharacterSaveTask.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "CharacterSaveTask.h"
|
||||
#include "RealmCharacter.h"
|
||||
|
||||
#include "../Common/ByteBufferReader.hpp"
|
||||
#include "../../logging.h"
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user