mirror of
https://github.com/HikikoMarmy/Champions-Reborn-Server.git
synced 2026-04-04 16:49:47 -03:00
Reorganized and cleaned up the solution.
This commit is contained in:
450
Source/Common/ByteStream.cpp
Normal file
450
Source/Common/ByteStream.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
#include "Common/ByteStream.hpp"
|
||||
|
||||
#include <codecvt>
|
||||
#include <span>
|
||||
|
||||
ByteBuffer::ByteBuffer( const std::vector< uint8_t > &data )
|
||||
{
|
||||
this->m_buffer = data;
|
||||
this->m_position = 0;
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer( const std::string &data )
|
||||
{
|
||||
this->m_buffer = std::vector< uint8_t >( data.begin(), data.end() );
|
||||
this->m_position = 0;
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer( const uint8_t *data, uint32_t length )
|
||||
{
|
||||
this->m_buffer = std::vector< uint8_t >( data, data + length );
|
||||
this->m_position = 0;
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer( uint32_t length )
|
||||
{
|
||||
this->m_buffer = std::vector< uint8_t >( length, 0 );
|
||||
this->m_position = 0;
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer()
|
||||
{
|
||||
this->m_position = 0;
|
||||
}
|
||||
|
||||
ByteBuffer::~ByteBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void ByteBuffer::resize( uint32_t size )
|
||||
{
|
||||
m_buffer.resize( size );
|
||||
}
|
||||
|
||||
void ByteBuffer::shrink_to_fit()
|
||||
{
|
||||
m_buffer.shrink_to_fit();
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
void ByteBuffer::write( T value )
|
||||
{
|
||||
write_bytes( ( uint8_t * )&value, sizeof( T ) );
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
T ByteBuffer::read()
|
||||
{
|
||||
if( m_position >= m_buffer.size() )
|
||||
{
|
||||
return (T)0;
|
||||
}
|
||||
|
||||
T value = *( T * )&m_buffer[ m_position ];
|
||||
m_position += sizeof( T );
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ByteBuffer::write_utf8( const std::string &str, std::optional<uint32_t> length )
|
||||
{
|
||||
if( length.has_value() )
|
||||
{
|
||||
write_u32( length.value() );
|
||||
|
||||
if( length.value() > str.size() )
|
||||
{
|
||||
write_bytes( std::vector< uint8_t >( str.begin(), str.end() ) );
|
||||
write_bytes( std::vector< uint8_t >( length.value() - str.size(), 0 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
write_bytes( std::vector< uint8_t >( str.begin(), str.begin() + length.value() ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
write_u32( static_cast< uint32_t >( str.size() ) );
|
||||
write_bytes( std::vector< uint8_t >( str.begin(), str.end() ) );
|
||||
}
|
||||
}
|
||||
|
||||
void ByteBuffer::write_utf16( const std::wstring &str, std::optional<uint32_t> length )
|
||||
{
|
||||
write_u32( static_cast< uint32_t >( str.size() ) );
|
||||
|
||||
for( wchar_t ch : str )
|
||||
{
|
||||
uint16_t val = static_cast< uint16_t >( ch );
|
||||
write<uint8_t>( val & 0xFF );
|
||||
write<uint8_t>( ( val >> 8 ) & 0xFF );
|
||||
}
|
||||
}
|
||||
|
||||
void ByteBuffer::write_sz_utf8( const std::string &str, std::optional<uint32_t> length )
|
||||
{
|
||||
if( length )
|
||||
{
|
||||
write_bytes( std::vector< uint8_t >( str.begin(), str.end() ) );
|
||||
write_bytes( std::vector< uint8_t >( length.value() - str.size(), 0 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
write_bytes( std::vector< uint8_t >( str.begin(), str.end() ) );
|
||||
write< uint8_t >( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
void ByteBuffer::write_sz_utf16( const std::wstring &str, std::optional<uint32_t> length )
|
||||
{
|
||||
for( wchar_t ch : str )
|
||||
{
|
||||
uint16_t val = static_cast< uint16_t >( ch );
|
||||
write<uint8_t>( val & 0xFF );
|
||||
write<uint8_t>( ( val >> 8 ) & 0xFF );
|
||||
}
|
||||
|
||||
if( length )
|
||||
{
|
||||
size_t bytesWritten = str.size() * 2;
|
||||
size_t totalBytes = length.value();
|
||||
|
||||
if( bytesWritten < totalBytes )
|
||||
{
|
||||
write_bytes( std::vector<uint8_t>( totalBytes - bytesWritten, 0 ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
write<uint16_t>( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
void ByteBuffer::write_encrypted_utf8( const std::string &str )
|
||||
{
|
||||
auto encrypted = RealmCrypt::encryptSymmetric( std::vector< uint8_t >( str.begin(), str.end() ) );
|
||||
|
||||
write_u32( static_cast< uint32_t >( encrypted.size() ) + 4 );
|
||||
write_u32( static_cast< uint32_t >( str.size() ) );
|
||||
|
||||
write_bytes( encrypted );
|
||||
}
|
||||
|
||||
void ByteBuffer::write_encrypted_utf16( const std::wstring &str )
|
||||
{
|
||||
std::vector< uint8_t > utf16;
|
||||
for( auto c : str )
|
||||
{
|
||||
utf16.push_back( c & 0xFF );
|
||||
utf16.push_back( ( c >> 8 ) & 0xFF );
|
||||
}
|
||||
|
||||
auto encrypted = RealmCrypt::encryptSymmetric( utf16 );
|
||||
uint32_t encryptedLength = static_cast< uint32_t >( encrypted.size() );
|
||||
uint32_t decryptedLength = static_cast< uint32_t >( utf16.size() );
|
||||
|
||||
// Correct blockLength: in 2-byte words, including the 4-byte decrypted length
|
||||
write_u32( ( encryptedLength + 4 ) / 2 );
|
||||
write_u32( decryptedLength );
|
||||
|
||||
write_bytes( encrypted );
|
||||
}
|
||||
|
||||
uint8_t ByteBuffer::read_u8()
|
||||
{
|
||||
return read< uint8_t >();
|
||||
}
|
||||
|
||||
uint16_t ByteBuffer::read_u16()
|
||||
{
|
||||
return read< uint16_t >();
|
||||
}
|
||||
|
||||
uint32_t ByteBuffer::read_u32()
|
||||
{
|
||||
return read< uint32_t >();
|
||||
}
|
||||
|
||||
int8_t ByteBuffer::read_i8()
|
||||
{
|
||||
return read< int8_t >();
|
||||
}
|
||||
|
||||
int16_t ByteBuffer::read_i16()
|
||||
{
|
||||
return read< int16_t >();
|
||||
}
|
||||
|
||||
int32_t ByteBuffer::read_i32()
|
||||
{
|
||||
return read< int32_t >();
|
||||
}
|
||||
|
||||
float_t ByteBuffer::read_f32()
|
||||
{
|
||||
return read< float_t >();
|
||||
}
|
||||
|
||||
std::string ByteBuffer::read_utf8( std::optional<uint32_t> length )
|
||||
{
|
||||
if( !length )
|
||||
{
|
||||
length = read_u32();
|
||||
}
|
||||
|
||||
if( m_position + length.value() > m_buffer.size() )
|
||||
{
|
||||
throw std::runtime_error( "read_utf8: Attempt to read past end of buffer" );
|
||||
}
|
||||
|
||||
std::string value( reinterpret_cast< const char * >( &m_buffer[ m_position ] ), length.value() );
|
||||
m_position += length.value();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::wstring ByteBuffer::read_utf16( std::optional<uint32_t> length )
|
||||
{
|
||||
if( !length )
|
||||
{
|
||||
length = read_u32();
|
||||
}
|
||||
|
||||
uint32_t byteLength = length.value() * 2;
|
||||
|
||||
if( m_position + byteLength > m_buffer.size() )
|
||||
{
|
||||
throw std::runtime_error( "read_utf16: Attempt to read past end of buffer" );
|
||||
}
|
||||
|
||||
std::wstring value;
|
||||
value.reserve( length.value() );
|
||||
|
||||
for( size_t i = 0; i < byteLength; i += 2 )
|
||||
{
|
||||
uint16_t ch = m_buffer[ m_position + i ] | ( m_buffer[ m_position + i + 1 ] << 8 );
|
||||
value.push_back( static_cast< wchar_t >( ch ) );
|
||||
}
|
||||
|
||||
m_position += byteLength;
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string ByteBuffer::read_sz_utf8()
|
||||
{
|
||||
std::string value;
|
||||
while( m_buffer[ m_position ] != 0 )
|
||||
{
|
||||
value.push_back( m_buffer[ m_position ] );
|
||||
m_position++;
|
||||
}
|
||||
|
||||
m_position++;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::wstring ByteBuffer::read_sz_utf16()
|
||||
{
|
||||
std::wstring value;
|
||||
while( m_buffer[ m_position ] != 0 || m_buffer[ m_position + 1 ] != 0 )
|
||||
{
|
||||
value.push_back( m_buffer[ m_position ] | ( m_buffer[ m_position + 1 ] << 8 ) );
|
||||
m_position += 2;
|
||||
}
|
||||
|
||||
m_position += 2;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string ByteBuffer::read_encrypted_utf8( bool hasBlockLength )
|
||||
{
|
||||
uint32_t encryptedLength = 0;
|
||||
uint32_t decryptedLength = 0;
|
||||
|
||||
if( hasBlockLength )
|
||||
{
|
||||
uint32_t blockLength = read_u32() * 2;
|
||||
decryptedLength = read_u32();
|
||||
encryptedLength = blockLength - 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
decryptedLength = read_u32();
|
||||
encryptedLength = Util::round_up( decryptedLength, 16 );
|
||||
}
|
||||
|
||||
std::span< const uint8_t > encryptedBuffer( m_buffer.data() + m_position, encryptedLength );
|
||||
|
||||
m_position += encryptedLength;
|
||||
|
||||
if( decryptedLength == 0 )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
// Decrypt the buffer
|
||||
std::vector< uint8_t > decryptedBuffer = RealmCrypt::decryptSymmetric( encryptedBuffer );
|
||||
|
||||
std::string result( decryptedBuffer.begin(), decryptedBuffer.end() );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring ByteBuffer::read_encrypted_utf16( bool hasBlockLength )
|
||||
{
|
||||
uint32_t encryptedLength = 0;
|
||||
uint32_t decryptedLength = 0;
|
||||
|
||||
if( hasBlockLength )
|
||||
{
|
||||
uint32_t blockLength = read_u32() * 2;
|
||||
decryptedLength = read_u32(); // This length is already multiplied by sizeof(wchar_t)
|
||||
encryptedLength = blockLength - 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
decryptedLength = read_u32();
|
||||
encryptedLength = Util::round_up( decryptedLength, 16 );
|
||||
}
|
||||
|
||||
std::span< const uint8_t > encryptedBuffer( m_buffer.data() + m_position, encryptedLength );
|
||||
|
||||
m_position += encryptedLength;
|
||||
|
||||
if( decryptedLength == 0 )
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
|
||||
// Decrypt the buffer
|
||||
std::vector< uint8_t > decryptedBuffer = RealmCrypt::decryptSymmetric( encryptedBuffer );
|
||||
|
||||
std::wstring result( decryptedLength / 2, L'\0' );
|
||||
std::memcpy( result.data(), decryptedBuffer.data(), decryptedLength );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ByteBuffer::write_bytes( const std::vector< uint8_t > &value )
|
||||
{
|
||||
std::copy( value.begin(), value.end(), std::back_inserter( m_buffer ) );
|
||||
m_position += value.size();
|
||||
}
|
||||
|
||||
void ByteBuffer::write_bytes( const uint8_t *value, uint32_t length )
|
||||
{
|
||||
std::copy( value, value + length, std::back_inserter( m_buffer ) );
|
||||
m_position += length;
|
||||
}
|
||||
|
||||
void ByteBuffer::write_encrypted_bytes( const std::vector<uint8_t> &value )
|
||||
{
|
||||
auto encrypted = RealmCrypt::encryptSymmetric( value );
|
||||
|
||||
write_u32( static_cast< uint32_t >( encrypted.size() ) + 4 );
|
||||
write_u32( static_cast< uint32_t >( value.size() ) );
|
||||
|
||||
write_bytes( encrypted );
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ByteBuffer::read_bytes( uint32_t length )
|
||||
{
|
||||
std::vector<uint8_t> value( length, 0 );
|
||||
|
||||
std::copy( m_buffer.begin() + m_position, m_buffer.begin() + m_position + length, value.begin() );
|
||||
|
||||
m_position += length;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ByteBuffer::read_encrypted_bytes( uint32_t length )
|
||||
{
|
||||
std::vector< uint8_t > encrypted = read_bytes( length );
|
||||
|
||||
auto decrypted = RealmCrypt::decryptSymmetric( encrypted );
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ByteBuffer::get_buffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
size_t ByteBuffer::get_length() const
|
||||
{
|
||||
return m_buffer.size();
|
||||
}
|
||||
|
||||
void ByteBuffer::set_position( size_t where )
|
||||
{
|
||||
if( where > m_buffer.size() )
|
||||
{
|
||||
where = m_buffer.size();
|
||||
}
|
||||
|
||||
this->m_position = where;
|
||||
}
|
||||
|
||||
size_t ByteBuffer::get_position() const
|
||||
{
|
||||
return this->m_position;
|
||||
}
|
||||
|
||||
void ByteBuffer::write_u8( uint8_t value )
|
||||
{
|
||||
write< uint8_t >( value );
|
||||
}
|
||||
|
||||
void ByteBuffer::write_u16( uint16_t value )
|
||||
{
|
||||
write< uint16_t >( value );
|
||||
}
|
||||
|
||||
void ByteBuffer::write_u32( uint32_t value )
|
||||
{
|
||||
write< uint32_t >( value );
|
||||
}
|
||||
|
||||
void ByteBuffer::write_i8( int8_t value )
|
||||
{
|
||||
write< int8_t >( value );
|
||||
}
|
||||
|
||||
void ByteBuffer::write_i16( int16_t value )
|
||||
{
|
||||
write< int16_t >( value );
|
||||
}
|
||||
|
||||
void ByteBuffer::write_i32( int32_t value )
|
||||
{
|
||||
write< int32_t >( value );
|
||||
}
|
||||
|
||||
void ByteBuffer::write_f32( float_t value )
|
||||
{
|
||||
write< float_t >( value );
|
||||
}
|
||||
97
Source/Common/Utility.cpp
Normal file
97
Source/Common/Utility.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "Common/Utility.hpp"
|
||||
|
||||
int32_t Util::round_up( int32_t numToRound, int32_t multiple )
|
||||
{
|
||||
if( multiple == 0 )
|
||||
return numToRound;
|
||||
|
||||
int32_t remainder = abs( numToRound ) % multiple;
|
||||
if( remainder == 0 )
|
||||
return numToRound;
|
||||
|
||||
if( numToRound < 0 )
|
||||
return -( abs( numToRound ) - remainder );
|
||||
else
|
||||
return numToRound + multiple - remainder;
|
||||
}
|
||||
|
||||
int32_t Util::round_down( int32_t numToRound, int32_t multiple )
|
||||
{
|
||||
if( multiple == 0 )
|
||||
return numToRound;
|
||||
|
||||
int32_t remainder = abs( numToRound ) % multiple;
|
||||
if( remainder == 0 )
|
||||
return numToRound;
|
||||
|
||||
if( numToRound < 0 )
|
||||
return -( abs( numToRound ) + remainder );
|
||||
else
|
||||
return numToRound - remainder;
|
||||
}
|
||||
|
||||
uint16_t Util::ByteSwap( uint16_t val )
|
||||
{
|
||||
return ( val << 8 ) | ( val >> 8 );
|
||||
}
|
||||
|
||||
uint32_t Util::ByteSwap( uint32_t val )
|
||||
{
|
||||
return ( ( val << 24 ) & 0xFF000000 ) |
|
||||
( ( val << 8 ) & 0x00FF0000 ) |
|
||||
( ( val >> 8 ) & 0x0000FF00 ) |
|
||||
( ( val >> 24 ) & 0x000000FF );
|
||||
}
|
||||
|
||||
std::string Util::IPFromAddr( const sockaddr_in &addr )
|
||||
{
|
||||
char buffer[ INET_ADDRSTRLEN ] = {};
|
||||
const char *result = inet_ntop( AF_INET, &addr.sin_addr, buffer, sizeof( buffer ) );
|
||||
|
||||
if( result )
|
||||
return std::string( buffer );
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string Util::WideToUTF8( const std::wstring &wstr )
|
||||
{
|
||||
if( wstr.empty() ) return {};
|
||||
|
||||
int size_needed = WideCharToMultiByte(
|
||||
CP_UTF8, 0, wstr.c_str(), static_cast< int >( wstr.size() ),
|
||||
nullptr, 0, nullptr, nullptr
|
||||
);
|
||||
|
||||
std::string result( size_needed, 0 );
|
||||
|
||||
WideCharToMultiByte(
|
||||
CP_UTF8, 0, wstr.c_str(), static_cast< int >( wstr.size() ),
|
||||
&result[ 0 ], size_needed, nullptr, nullptr
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring Util::UTF8ToWide( const std::string &str )
|
||||
{
|
||||
if( str.empty() ) return {};
|
||||
int size_needed = MultiByteToWideChar(
|
||||
CP_UTF8, 0, str.c_str(), static_cast< int >( str.size() ),
|
||||
nullptr, 0
|
||||
);
|
||||
std::wstring result( size_needed, 0 );
|
||||
MultiByteToWideChar(
|
||||
CP_UTF8, 0, str.c_str(), static_cast< int >( str.size() ),
|
||||
&result[ 0 ], size_needed
|
||||
);
|
||||
return result;
|
||||
}
|
||||
53
Source/Crypto/PasswordHash.cpp
Normal file
53
Source/Crypto/PasswordHash.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Crypto/PasswordHash.hpp"
|
||||
|
||||
std::string HexDump( const std::vector<uint8_t> &bytes )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
for( auto b : bytes )
|
||||
oss << std::hex << std::setfill( '0' ) << std::setw( 2 ) << ( int )b;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string HashPassword( const std::string &password, uint32_t iterations, size_t saltLen )
|
||||
{
|
||||
std::vector<uint8_t> salt( saltLen );
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 rng( rd() );
|
||||
std::uniform_int_distribution<int32_t> dist( 0, 255 );
|
||||
|
||||
for( auto &b : salt ) b = static_cast< int32_t >( dist( rng ) );
|
||||
|
||||
auto derived = pbkdf2_hmac_sha256( password, salt, iterations, 32 );
|
||||
|
||||
return "pbkdf2$" + std::to_string( iterations ) + "$" +
|
||||
Base64Encode( salt ) + "$" +
|
||||
Base64Encode( derived );
|
||||
|
||||
}
|
||||
|
||||
bool VerifyPassword( const std::string &password, const std::string &storedHash )
|
||||
{
|
||||
auto parts = std::vector<std::string>();
|
||||
std::stringstream ss( storedHash );
|
||||
std::string token;
|
||||
|
||||
while( std::getline( ss, token, '$' ) )
|
||||
parts.push_back( token );
|
||||
|
||||
if( parts.size() != 4 || parts[ 0 ] != "pbkdf2" )
|
||||
throw std::runtime_error( "Invalid hash format" );
|
||||
|
||||
uint32_t iterations = std::stoul( parts[ 1 ] );
|
||||
std::vector<uint8_t> salt = Base64Decode( parts[ 2 ] );
|
||||
std::vector<uint8_t> expected = Base64Decode( parts[ 3 ] );
|
||||
|
||||
auto derived = pbkdf2_hmac_sha256( password, salt, iterations, expected.size() );
|
||||
|
||||
return derived == expected;
|
||||
}
|
||||
163
Source/Crypto/RealmCrypt.cpp
Normal file
163
Source/Crypto/RealmCrypt.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
#include "Crypto/RealmCrypt.hpp"
|
||||
#include "Common/Utility.hpp"
|
||||
|
||||
RealmCrypt::RealmCrypt()
|
||||
{
|
||||
std::srand( static_cast< unsigned >( std::time( nullptr ) ) );
|
||||
}
|
||||
|
||||
std::vector< uint8_t > RealmCrypt::generateSymmetricKey( void )
|
||||
{
|
||||
constexpr size_t KEY_LENGTH = 32;
|
||||
|
||||
std::vector< uint8_t > keyData( KEY_LENGTH, 0 );
|
||||
|
||||
// Generate 32 random bytes
|
||||
for( size_t i = 0; i < KEY_LENGTH; ++i )
|
||||
{
|
||||
keyData[ i ] = static_cast< uint8_t >( std::rand() % 255 );
|
||||
}
|
||||
|
||||
return keyData;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RealmCrypt::getSymmetricKey( void )
|
||||
{
|
||||
return default_sym_key;
|
||||
}
|
||||
|
||||
std::string RealmCrypt::encryptString( std::string &input )
|
||||
{
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
input.append( 16 - ( input.size() % 16 ), '\0' );
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.EncryptECB(
|
||||
reinterpret_cast< const uint8_t * >( input.c_str() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
return std::string( reinterpret_cast< const char * >( result ), input.size() );
|
||||
}
|
||||
|
||||
std::string RealmCrypt::decryptString( std::string &input )
|
||||
{
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
input.append( 16 - ( input.size() % 16 ), '\0' );
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.DecryptECB( reinterpret_cast< const uint8_t * >(
|
||||
input.c_str() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
return std::string( reinterpret_cast< const char * >( result ), input.size() );
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RealmCrypt::encryptString( const std::wstring &input )
|
||||
{
|
||||
// Convert UTF-16 string to raw bytes
|
||||
std::vector<uint8_t> utf16Bytes;
|
||||
utf16Bytes.reserve( input.size() * 2 );
|
||||
for( wchar_t ch : input )
|
||||
{
|
||||
utf16Bytes.push_back( static_cast< uint8_t >( ch & 0xFF ) );
|
||||
utf16Bytes.push_back( static_cast< uint8_t >( ( ch >> 8 ) & 0xFF ) );
|
||||
}
|
||||
|
||||
// Pad to nearest 16 bytes
|
||||
if( utf16Bytes.size() % 16 != 0 )
|
||||
{
|
||||
utf16Bytes.resize( ( utf16Bytes.size() / 16 + 1 ) * 16, 0 );
|
||||
}
|
||||
|
||||
// Encrypt using AES ECB
|
||||
rijndael aes;
|
||||
auto encrypted = aes.EncryptECB(
|
||||
reinterpret_cast< const uint8_t * >( utf16Bytes.data() ),
|
||||
static_cast< uint32_t >( utf16Bytes.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
// Return the raw encrypted bytes
|
||||
return std::vector<uint8_t>( encrypted, encrypted + utf16Bytes.size() );
|
||||
}
|
||||
|
||||
std::wstring RealmCrypt::decryptString( std::vector<uint8_t> &input )
|
||||
{
|
||||
// Ensure input is a multiple of 16 bytes
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
input.resize( ( input.size() / 16 + 1 ) * 16, 0 );
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.DecryptECB(
|
||||
reinterpret_cast< const uint8_t * >( input.data() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
// Convert decrypted bytes back into a wstring
|
||||
std::wstring output;
|
||||
output.reserve( input.size() / 2 );
|
||||
|
||||
for( size_t i = 0; i + 1 < input.size(); i += 2 )
|
||||
{
|
||||
uint16_t ch = static_cast< uint16_t >( input[ i ] | ( input[ i + 1 ] << 8 ) );
|
||||
if( ch == 0 ) break; // Optional: stop at null terminator
|
||||
output.push_back( static_cast< wchar_t >( ch ) );
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector< uint8_t > RealmCrypt::encryptSymmetric( std::span< const uint8_t > input )
|
||||
{
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
std::vector< uint8_t > paddedInput( input.begin(), input.end() );
|
||||
paddedInput.resize( ( ( input.size() / 16 ) + 1 ) * 16, 0 );
|
||||
input = paddedInput;
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.EncryptECB( reinterpret_cast< const uint8_t * >(
|
||||
input.data() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
return std::vector< uint8_t >( result, result + input.size() );
|
||||
}
|
||||
|
||||
std::vector< uint8_t > RealmCrypt::decryptSymmetric( std::span< const uint8_t > input )
|
||||
{
|
||||
if( input.size() % 16 != 0 )
|
||||
{
|
||||
std::vector< uint8_t > paddedInput( input.begin(), input.end() );
|
||||
paddedInput.resize( ( ( input.size() / 16 ) + 1 ) * 16, 0 );
|
||||
input = paddedInput;
|
||||
}
|
||||
|
||||
rijndael aes;
|
||||
|
||||
auto result = aes.DecryptECB( reinterpret_cast< const uint8_t * >(
|
||||
input.data() ),
|
||||
static_cast< uint32_t >( input.size() ),
|
||||
default_sym_key.data()
|
||||
);
|
||||
|
||||
return std::vector< uint8_t >( result, result + input.size() );
|
||||
}
|
||||
371
Source/Crypto/rijndael.cpp
Normal file
371
Source/Crypto/rijndael.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
#include "Crypto/rijndael.hpp"
|
||||
|
||||
rijndael::rijndael()
|
||||
{
|
||||
this->Nk = 8;
|
||||
this->Nr = 14;
|
||||
}
|
||||
|
||||
uint8_t *rijndael::EncryptECB( const uint8_t in[], uint32_t inLen,
|
||||
const uint8_t key[] )
|
||||
{
|
||||
CheckLength( inLen );
|
||||
uint8_t *out = new uint8_t[ inLen ];
|
||||
uint8_t *roundKeys = new uint8_t[ 4 * Nb * ( Nr + 1 ) ];
|
||||
KeyExpansion( key, roundKeys );
|
||||
for( uint32_t i = 0; i < inLen; i += blockBytesLen )
|
||||
{
|
||||
EncryptBlock( in + i, out + i, roundKeys );
|
||||
}
|
||||
|
||||
delete[] roundKeys;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
uint8_t *rijndael::DecryptECB( const uint8_t in[], uint32_t inLen,
|
||||
const uint8_t key[] )
|
||||
{
|
||||
CheckLength( inLen );
|
||||
uint8_t *out = new uint8_t[ inLen ];
|
||||
uint8_t *roundKeys = new uint8_t[ 4 * Nb * ( Nr + 1 ) ];
|
||||
KeyExpansion( key, roundKeys );
|
||||
|
||||
for( uint32_t i = 0; i < inLen; i += blockBytesLen )
|
||||
{
|
||||
DecryptBlock( in + i, out + i, roundKeys );
|
||||
}
|
||||
|
||||
delete[] roundKeys;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void rijndael::CheckLength( uint32_t len )
|
||||
{
|
||||
if( len % blockBytesLen != 0 )
|
||||
{
|
||||
throw std::length_error( "Plaintext length must be divisible by " +
|
||||
std::to_string( blockBytesLen ) );
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::EncryptBlock( const uint8_t in[], uint8_t out[],
|
||||
uint8_t *roundKeys )
|
||||
{
|
||||
uint8_t state[ 4 ][ Nb ];
|
||||
uint32_t i, j, round;
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
state[ i ][ j ] = in[ i + 4 * j ];
|
||||
}
|
||||
}
|
||||
|
||||
AddRoundKey( state, roundKeys );
|
||||
|
||||
for( round = 1; round <= Nr - 1; round++ )
|
||||
{
|
||||
SubBytes( state );
|
||||
ShiftRows( state );
|
||||
MixColumns( state );
|
||||
AddRoundKey( state, roundKeys + round * 4 * Nb );
|
||||
}
|
||||
|
||||
SubBytes( state );
|
||||
ShiftRows( state );
|
||||
AddRoundKey( state, roundKeys + Nr * 4 * Nb );
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
out[ i + 4 * j ] = state[ i ][ j ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::DecryptBlock( const uint8_t in[], uint8_t out[],
|
||||
uint8_t *roundKeys )
|
||||
{
|
||||
uint8_t state[ 4 ][ Nb ];
|
||||
uint32_t i, j, round;
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
state[ i ][ j ] = in[ i + 4 * j ];
|
||||
}
|
||||
}
|
||||
|
||||
AddRoundKey( state, roundKeys + Nr * 4 * Nb );
|
||||
|
||||
for( round = Nr - 1; round >= 1; round-- )
|
||||
{
|
||||
InvSubBytes( state );
|
||||
InvShiftRows( state );
|
||||
AddRoundKey( state, roundKeys + round * 4 * Nb );
|
||||
InvMixColumns( state );
|
||||
}
|
||||
|
||||
InvSubBytes( state );
|
||||
InvShiftRows( state );
|
||||
AddRoundKey( state, roundKeys );
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
out[ i + 4 * j ] = state[ i ][ j ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::SubBytes( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint8_t t;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
t = state[ i ][ j ];
|
||||
state[ i ][ j ] = sbox[ t / 16 ][ t % 16 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::ShiftRow( uint8_t state[ 4 ][ Nb ], uint32_t i,
|
||||
uint32_t n ) // shift row i on n write_positions
|
||||
{
|
||||
uint8_t tmp[ Nb ];
|
||||
for( uint32_t j = 0; j < Nb; j++ )
|
||||
{
|
||||
tmp[ j ] = state[ i ][ ( j + n ) % Nb ];
|
||||
}
|
||||
memcpy( state[ i ], tmp, Nb * sizeof( uint8_t ) );
|
||||
}
|
||||
|
||||
void rijndael::ShiftRows( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
ShiftRow( state, 1, 1 );
|
||||
ShiftRow( state, 2, 2 );
|
||||
ShiftRow( state, 3, 3 );
|
||||
}
|
||||
|
||||
uint8_t rijndael::xtime( uint8_t b ) // multiply on x
|
||||
{
|
||||
return ( b << 1 ) ^ ( ( ( b >> 7 ) & 1 ) * 0x1b );
|
||||
}
|
||||
|
||||
void rijndael::MixColumns( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
uint8_t temp_state[ 4 ][ Nb ];
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
memset( temp_state[ i ], 0, 4 );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
for( size_t k = 0; k < 4; ++k )
|
||||
{
|
||||
for( size_t j = 0; j < 4; ++j )
|
||||
{
|
||||
if( CMDS[ i ][ k ] == 1 )
|
||||
temp_state[ i ][ j ] ^= state[ k ][ j ];
|
||||
else
|
||||
temp_state[ i ][ j ] ^= GF_MUL_TABLE[ CMDS[ i ][ k ] ][ state[ k ][ j ] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
memcpy( state[ i ], temp_state[ i ], 4 );
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::AddRoundKey( uint8_t state[ 4 ][ Nb ], uint8_t *key )
|
||||
{
|
||||
uint32_t i, j;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
state[ i ][ j ] = state[ i ][ j ] ^ key[ i + 4 * j ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::SubWord( uint8_t *a )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
a[ i ] = sbox[ a[ i ] / 16 ][ a[ i ] % 16 ];
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::RotWord( uint8_t *a )
|
||||
{
|
||||
uint8_t c = a[ 0 ];
|
||||
a[ 0 ] = a[ 1 ];
|
||||
a[ 1 ] = a[ 2 ];
|
||||
a[ 2 ] = a[ 3 ];
|
||||
a[ 3 ] = c;
|
||||
}
|
||||
|
||||
void rijndael::XorWords( uint8_t *a, uint8_t *b, uint8_t *c )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
c[ i ] = a[ i ] ^ b[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::Rcon( uint8_t *a, uint32_t n )
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t c = 1;
|
||||
for( i = 0; i < n - 1; i++ )
|
||||
{
|
||||
c = xtime( c );
|
||||
}
|
||||
|
||||
a[ 0 ] = c;
|
||||
a[ 1 ] = a[ 2 ] = a[ 3 ] = 0;
|
||||
}
|
||||
|
||||
void rijndael::KeyExpansion( const uint8_t key[], uint8_t w[] )
|
||||
{
|
||||
uint8_t temp[ 4 ];
|
||||
uint8_t rcon[ 4 ];
|
||||
|
||||
uint32_t i = 0;
|
||||
while( i < 4 * Nk )
|
||||
{
|
||||
w[ i ] = key[ i ];
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 4 * Nk;
|
||||
while( i < 4 * Nb * ( Nr + 1 ) )
|
||||
{
|
||||
temp[ 0 ] = w[ i - 4 + 0 ];
|
||||
temp[ 1 ] = w[ i - 4 + 1 ];
|
||||
temp[ 2 ] = w[ i - 4 + 2 ];
|
||||
temp[ 3 ] = w[ i - 4 + 3 ];
|
||||
|
||||
if( i / 4 % Nk == 0 )
|
||||
{
|
||||
RotWord( temp );
|
||||
SubWord( temp );
|
||||
Rcon( rcon, i / ( Nk * 4 ) );
|
||||
XorWords( temp, rcon, temp );
|
||||
}
|
||||
else if( Nk > 6 && i / 4 % Nk == 4 )
|
||||
{
|
||||
SubWord( temp );
|
||||
}
|
||||
|
||||
w[ i + 0 ] = w[ i - 4 * Nk ] ^ temp[ 0 ];
|
||||
w[ i + 1 ] = w[ i + 1 - 4 * Nk ] ^ temp[ 1 ];
|
||||
w[ i + 2 ] = w[ i + 2 - 4 * Nk ] ^ temp[ 2 ];
|
||||
w[ i + 3 ] = w[ i + 3 - 4 * Nk ] ^ temp[ 3 ];
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::InvSubBytes( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint8_t t;
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
for( j = 0; j < Nb; j++ )
|
||||
{
|
||||
t = state[ i ][ j ];
|
||||
state[ i ][ j ] = inv_sbox[ t / 16 ][ t % 16 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::InvMixColumns( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
uint8_t temp_state[ 4 ][ Nb ];
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
memset( temp_state[ i ], 0, 4 );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
for( size_t k = 0; k < 4; ++k )
|
||||
{
|
||||
for( size_t j = 0; j < 4; ++j )
|
||||
{
|
||||
temp_state[ i ][ j ] ^= GF_MUL_TABLE[ INV_CMDS[ i ][ k ] ][ state[ k ][ j ] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
{
|
||||
memcpy( state[ i ], temp_state[ i ], 4 );
|
||||
}
|
||||
}
|
||||
|
||||
void rijndael::InvShiftRows( uint8_t state[ 4 ][ Nb ] )
|
||||
{
|
||||
ShiftRow( state, 1, Nb - 1 );
|
||||
ShiftRow( state, 2, Nb - 2 );
|
||||
ShiftRow( state, 3, Nb - 3 );
|
||||
}
|
||||
|
||||
void rijndael::XorBlocks( const uint8_t *a, const uint8_t *b,
|
||||
uint8_t *c, uint32_t len )
|
||||
{
|
||||
for( uint32_t i = 0; i < len; i++ )
|
||||
{
|
||||
c[ i ] = a[ i ] ^ b[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rijndael::ArrayToVector( uint8_t *a,
|
||||
uint32_t len )
|
||||
{
|
||||
std::vector<uint8_t> v( a, a + len * sizeof( uint8_t ) );
|
||||
return v;
|
||||
}
|
||||
|
||||
uint8_t *rijndael::VectorToArray( std::vector<uint8_t> &a )
|
||||
{
|
||||
return a.data();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rijndael::EncryptECB( std::vector<uint8_t> in,
|
||||
std::vector<uint8_t> key )
|
||||
{
|
||||
uint8_t *out = EncryptECB( VectorToArray( in ), ( uint32_t )in.size(),
|
||||
VectorToArray( key ) );
|
||||
std::vector<uint8_t> v = ArrayToVector( out, static_cast< uint32_t >( in.size() ) );
|
||||
delete[] out;
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rijndael::DecryptECB( std::vector<uint8_t> in,
|
||||
std::vector<uint8_t> key )
|
||||
{
|
||||
uint8_t *out = DecryptECB( VectorToArray( in ), ( uint32_t )in.size(),
|
||||
VectorToArray( key ) );
|
||||
std::vector<uint8_t> v = ArrayToVector( out, ( uint32_t )in.size() );
|
||||
delete[] out;
|
||||
return v;
|
||||
}
|
||||
624
Source/Database/Database.cpp
Normal file
624
Source/Database/Database.cpp
Normal file
@@ -0,0 +1,624 @@
|
||||
#include "Database/Database.hpp"
|
||||
|
||||
#include "Crypto/PasswordHash.hpp"
|
||||
#include "Game/RealmCharacter.hpp"
|
||||
#include "Game/RealmCharacterMetaKV.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
Database::Database()
|
||||
{
|
||||
const fs::path dbPath = "./database/game.db";
|
||||
const fs::path dbDir = dbPath.parent_path();
|
||||
|
||||
std::error_code ec;
|
||||
if( !dbDir.empty() && !fs::exists( dbDir ) )
|
||||
{
|
||||
if( !fs::create_directories( dbDir, ec ) )
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Failed to create database directory: " + ec.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if( sqlite3_open( dbPath.string().c_str(), &m_db ) != SQLITE_OK )
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Failed to open DB: " + std::string( sqlite3_errmsg( m_db ) )
|
||||
);
|
||||
}
|
||||
|
||||
CreateTables();
|
||||
PrepareStatements();
|
||||
}
|
||||
|
||||
Database::~Database()
|
||||
{
|
||||
FinalizeStatements();
|
||||
if( m_db )
|
||||
sqlite3_close( m_db );
|
||||
}
|
||||
|
||||
void Database::CreateTables()
|
||||
{
|
||||
Execute( "CREATE TABLE IF NOT EXISTS RealmUsers ("
|
||||
"account_id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"username TEXT UNIQUE NOT NULL,"
|
||||
"password TEXT NOT NULL,"
|
||||
"email_address TEXT NOT NULL,"
|
||||
"date_of_birth NOT NULL,"
|
||||
"chat_handle TEXT NOT NULL)" );
|
||||
|
||||
Execute( "CREATE TABLE IF NOT EXISTS RealmCharacters ("
|
||||
"account_id INTEGER NOT NULL,"
|
||||
"character_id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"meta_data BLOB NOT NULL,"
|
||||
"character_data BLOB NOT NULL)" );
|
||||
|
||||
Execute( "CREATE TABLE IF NOT EXISTS RealmSession ("
|
||||
"account_id INTEGER NOT NULL,"
|
||||
"session_id TEXT NOT NULL UNIQUE,"
|
||||
"character_id INTEGER NOT NULL,"
|
||||
"ip_address TEXT NOT NULL,"
|
||||
"expire_time INTEGER NOT NULL,"
|
||||
"PRIMARY KEY(session_id))" );
|
||||
|
||||
Execute( "CREATE TABLE IF NOT EXISTS UserFriendList ("
|
||||
"account_id INTEGER NOT NULL,"
|
||||
"friend_handle TEXT NOT NULL,"
|
||||
"PRIMARY KEY(account_id, friend_handle))" );
|
||||
|
||||
Execute( "CREATE TABLE IF NOT EXISTS UserIgnoredList ("
|
||||
"account_id INTEGER NOT NULL,"
|
||||
"ignore_handle TEXT NOT NULL,"
|
||||
"PRIMARY KEY(account_id, ignore_handle))" );
|
||||
}
|
||||
|
||||
void Database::PrepareStatements()
|
||||
{
|
||||
const std::vector<std::pair<QueryID, const char *>> queries = {
|
||||
{ QueryID::CreateAccount,
|
||||
"INSERT INTO RealmUsers ( username, password, email_address, date_of_birth, chat_handle ) VALUES ( ?, ?, ?, ?, ? );" },
|
||||
|
||||
{ QueryID::VerifyAccount,
|
||||
"SELECT account_id, username, password, chat_handle FROM RealmUsers WHERE username = ?;" },
|
||||
|
||||
{ QueryID::LoadAccount,
|
||||
"SELECT chat_handle FROM RealmUsers WHERE account_id = ?;" },
|
||||
|
||||
{ QueryID::CreateNewCharacter,
|
||||
"INSERT INTO RealmCharacters ( account_id, meta_data, character_data ) VALUES ( ?, ?, ? );" },
|
||||
|
||||
{ QueryID::SaveCharacter,
|
||||
"UPDATE RealmCharacters SET meta_data = ?, character_data = ? WHERE account_id = ? AND character_id = ?;" },
|
||||
|
||||
{ QueryID::LoadCharacter,
|
||||
"SELECT character_id, meta_data, character_data FROM RealmCharacters WHERE account_id = ? AND character_id = ?;" },
|
||||
|
||||
{ QueryID::LoadCharacterSlots,
|
||||
"SELECT character_id, meta_data FROM RealmCharacters WHERE account_id = ? ORDER BY character_id;" },
|
||||
|
||||
{ QueryID::SaveFriend,
|
||||
"INSERT OR IGNORE INTO UserFriendList ( account_id, friend_handle ) VALUES ( ?, ? );" },
|
||||
|
||||
{ QueryID::RemoveFriend,
|
||||
"DELETE FROM UserFriendList WHERE account_id = ? AND friend_handle = ?;" },
|
||||
|
||||
{ QueryID::LoadFriendList,
|
||||
"SELECT friend_handle FROM UserFriendList WHERE account_id = ?;" },
|
||||
|
||||
{ QueryID::SaveIgnore,
|
||||
"INSERT OR IGNORE INTO UserIgnoredList ( account_id, ignore_handle ) VALUES ( ?, ? );" },
|
||||
|
||||
{ QueryID::RemoveIgnore,
|
||||
"DELETE FROM UserIgnoredList WHERE account_id = ? AND ignore_handle = ?;" },
|
||||
|
||||
{ QueryID::LoadIgnoreList,
|
||||
"SELECT ignore_handle FROM UserIgnoredList WHERE account_id = ?;" }
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
for( const auto &[id, sql] : queries )
|
||||
{
|
||||
sqlite3_stmt *stmt = nullptr;
|
||||
if( sqlite3_prepare_v2( m_db, sql, -1, &stmt, nullptr ) != SQLITE_OK )
|
||||
{
|
||||
throw std::runtime_error( "Failed to prepare statement: " + std::string( sqlite3_errmsg( m_db ) ) );
|
||||
}
|
||||
m_statements[ id ] = stmt;
|
||||
}
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
FinalizeStatements();
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
}
|
||||
|
||||
void Database::FinalizeStatements()
|
||||
{
|
||||
if( m_db == nullptr )
|
||||
return;
|
||||
|
||||
for( const auto &[id, stmt] : m_statements )
|
||||
{
|
||||
if( stmt ) sqlite3_finalize( stmt );
|
||||
}
|
||||
m_statements.clear();
|
||||
}
|
||||
|
||||
void Database::Execute( const char *sql )
|
||||
{
|
||||
try
|
||||
{
|
||||
char *errMsg = nullptr;
|
||||
if( sqlite3_exec( m_db, sql, nullptr, nullptr, &errMsg ) != SQLITE_OK )
|
||||
{
|
||||
std::string error = "SQL execution failed: " + std::string( errMsg );
|
||||
sqlite3_free( errMsg );
|
||||
throw std::runtime_error( error );
|
||||
}
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
}
|
||||
|
||||
int64_t Database::CreateNewAccount( const std::string &username,
|
||||
const std::string &password,
|
||||
const std::string &email_address,
|
||||
const std::string &date_of_birth,
|
||||
const std::string &chat_handle )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto hashedPassword = HashPassword( password, 1000, 32 );
|
||||
auto stmt = m_statements[ QueryID::CreateAccount ];
|
||||
|
||||
SQLiteTransaction tx( m_db );
|
||||
{
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
|
||||
sqlite3_bind_text( stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT );
|
||||
sqlite3_bind_text( stmt, 2, hashedPassword.c_str(), -1, SQLITE_TRANSIENT );
|
||||
sqlite3_bind_text( stmt, 3, email_address.c_str(), -1, SQLITE_TRANSIENT );
|
||||
sqlite3_bind_text( stmt, 4, date_of_birth.c_str(), -1, SQLITE_TRANSIENT );
|
||||
sqlite3_bind_text( stmt, 5, chat_handle.c_str(), -1, SQLITE_TRANSIENT );
|
||||
|
||||
if( sqlite3_step( stmt ) != SQLITE_DONE )
|
||||
{
|
||||
throw std::runtime_error( "Insert failed: " + std::string( sqlite3_errmsg( m_db ) ) );
|
||||
}
|
||||
}
|
||||
tx.commit();
|
||||
|
||||
return sqlite3_last_insert_rowid( m_db );
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: " + std::string( e.what() ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::tuple< bool, int64_t, std::wstring >
|
||||
Database::VerifyAccount( const std::wstring &username, const std::wstring &password )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::VerifyAccount ];
|
||||
auto username_utf8 = Util::WideToUTF8( username );
|
||||
auto password_utf8 = Util::WideToUTF8( password );
|
||||
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
|
||||
sqlite3_bind_text( stmt, 1, username_utf8.c_str(), -1, SQLITE_TRANSIENT );
|
||||
|
||||
// Execute the statement
|
||||
if( sqlite3_step( stmt ) == SQLITE_ROW )
|
||||
{
|
||||
int64_t accountId = sqlite3_column_int64( stmt, 0 );
|
||||
const char *dbUsername = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 1 ) );
|
||||
const char *dbPassword = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 2 ) );
|
||||
const char *dbChatHandle = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 3 ) );
|
||||
|
||||
if( username_utf8 == dbUsername && VerifyPassword( password_utf8, dbPassword ) )
|
||||
{
|
||||
Log::Debug( "Account verified: {} (ID: {})", username, accountId );
|
||||
return std::make_tuple( true, accountId, Util::UTF8ToWide( dbChatHandle ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::Debug( "Invalid credentials for account ID: {}", accountId );
|
||||
return std::make_tuple( false, -1, L"" );
|
||||
}
|
||||
}
|
||||
else if( sqlite3_step( stmt ) == SQLITE_DONE )
|
||||
{
|
||||
return std::make_tuple( false, -1, L"" ); // No matching account found
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error( "Query failed: " + std::string( sqlite3_errmsg( m_db ) ) );
|
||||
}
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
|
||||
return std::make_tuple( false, -1, L"" );
|
||||
}
|
||||
|
||||
uint32_t Database::CreateNewCharacter( const int64_t account_id, const CharacterSlotData meta, const std::vector< uint8_t > &blob )
|
||||
{
|
||||
if( account_id <= 0 || meta.empty() || blob.empty() )
|
||||
{
|
||||
Log::Error( "Invalid parameters for CreateNewCharacter" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::CreateNewCharacter ];
|
||||
const auto meta_data = meta.Serialize();
|
||||
|
||||
SQLiteTransaction tx( m_db );
|
||||
{
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
sqlite3_bind_blob( stmt, 2, meta_data.data(), static_cast< int >( meta_data.size() ), SQLITE_STATIC );
|
||||
sqlite3_bind_blob( stmt, 3, blob.data(), static_cast< int >( blob.size() ), SQLITE_STATIC );
|
||||
|
||||
int rc = sqlite3_step( stmt );
|
||||
if( rc != SQLITE_DONE )
|
||||
{
|
||||
Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
tx.commit();
|
||||
|
||||
uint32_t character_id = static_cast< uint32_t >( sqlite3_last_insert_rowid( m_db ) );
|
||||
|
||||
return character_id;
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Database::SaveCharacter( const int64_t account_id, const int32_t character_id, const CharacterSlotData meta, const std::vector< uint8_t > &blob )
|
||||
{
|
||||
if( account_id <= 0 || character_id <= 0 || meta.empty() || blob.empty() )
|
||||
{
|
||||
Log::Error( "Invalid parameters for SaveCharacter" );
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::SaveCharacter ];
|
||||
const auto meta_data = meta.Serialize();
|
||||
|
||||
SQLiteTransaction tx( m_db );
|
||||
{
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
|
||||
sqlite3_bind_blob( stmt, 1, meta_data.data(), static_cast< int >( meta_data.size() ), SQLITE_STATIC );
|
||||
sqlite3_bind_blob( stmt, 2, blob.data(), static_cast< int >( blob.size() ), SQLITE_STATIC );
|
||||
sqlite3_bind_int64( stmt, 3, account_id );
|
||||
sqlite3_bind_int( stmt, 4, character_id );
|
||||
|
||||
if( sqlite3_step( stmt ) != SQLITE_DONE )
|
||||
{
|
||||
throw std::runtime_error( "Update failed: " + std::string( sqlite3_errmsg( m_db ) ) );
|
||||
}
|
||||
}
|
||||
tx.commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map< uint32_t, CharacterSlotData > Database::LoadCharacterSlots( const int64_t account_id )
|
||||
{
|
||||
std::map< uint32_t, CharacterSlotData > result;
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::LoadCharacterSlots ];
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
|
||||
while( sqlite3_step( stmt ) == SQLITE_ROW )
|
||||
{
|
||||
const auto character_id = sqlite3_column_int( stmt, 0 );
|
||||
|
||||
const auto *slot_data_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 1 ) );
|
||||
const auto slot_data_size = sqlite3_column_bytes( stmt, 1 );
|
||||
|
||||
if( slot_data_blob == nullptr || slot_data_size == 0 )
|
||||
{
|
||||
Log::Error( "Character ID {} has no metadata", character_id );
|
||||
continue;
|
||||
}
|
||||
|
||||
CharacterSlotData slot_data( std::vector< uint8_t >( slot_data_blob, slot_data_blob + slot_data_size ) );
|
||||
|
||||
result.insert( { character_id, slot_data } );
|
||||
}
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
result.clear();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
sptr_realm_character Database::LoadCharacterData( const int64_t account_id, const int32_t character_id )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto data = std::make_shared< RealmCharacter >();
|
||||
auto stmt = m_statements[ QueryID::LoadCharacter ];
|
||||
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
sqlite3_bind_int( stmt, 2, character_id );
|
||||
|
||||
if( sqlite3_step( stmt ) == SQLITE_ROW )
|
||||
{
|
||||
const auto character_id = sqlite3_column_int( stmt, 0 );
|
||||
|
||||
const auto *meta_data_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 1 ) );
|
||||
const auto meta_data_size = sqlite3_column_bytes( stmt, 1 );
|
||||
|
||||
if( meta_data_blob && meta_data_size > 0 )
|
||||
{
|
||||
data->Deserialize( std::vector< uint8_t >( meta_data_blob, meta_data_blob + meta_data_size ) );
|
||||
}
|
||||
|
||||
const auto *character_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 2 ) );
|
||||
const auto character_size = sqlite3_column_bytes( stmt, 2 );
|
||||
|
||||
if( character_blob && character_size > 0 )
|
||||
{
|
||||
data->m_characterId = character_id;
|
||||
data->m_data = std::vector< uint8_t >( character_blob, character_blob + character_size );
|
||||
}
|
||||
|
||||
Log::Debug( "Character data loaded for account ID: {} and character ID: {}", account_id, character_id );
|
||||
return data;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::Error( "No character data found for account ID: {} and character ID: {}", account_id, character_id );
|
||||
}
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Database::SaveFriend( const int64_t account_id, const std::wstring &friend_handle )
|
||||
{
|
||||
if( account_id <= 0 || friend_handle.empty() )
|
||||
{
|
||||
Log::Error( "Invalid parameters for SaveFriend" );
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::SaveFriend ];
|
||||
auto friendHandle = Util::WideToUTF8( friend_handle );
|
||||
SQLiteTransaction tx( m_db );
|
||||
{
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
sqlite3_bind_text( stmt, 2, friendHandle.c_str(), -1, SQLITE_TRANSIENT );
|
||||
|
||||
if( sqlite3_step( stmt ) != SQLITE_DONE )
|
||||
{
|
||||
Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
tx.commit();
|
||||
return true;
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Database::RemoveFriend( const int64_t account_id, const std::wstring &friend_handle )
|
||||
{
|
||||
if( account_id <= 0 || friend_handle.empty() )
|
||||
{
|
||||
Log::Error( "Invalid parameters for RemoveFriend" );
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::RemoveFriend ];
|
||||
auto friendHandle = Util::WideToUTF8( friend_handle );
|
||||
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
sqlite3_bind_text( stmt, 2, friendHandle.c_str(), -1, SQLITE_TRANSIENT );
|
||||
|
||||
if( sqlite3_step( stmt ) != SQLITE_DONE )
|
||||
{
|
||||
Log::Error( "SQLite delete failed: {}", sqlite3_errmsg( m_db ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> Database::LoadFriends( const int64_t account_id )
|
||||
{
|
||||
std::vector<std::wstring> friend_list;
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::LoadFriendList ];
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
|
||||
while( sqlite3_step( stmt ) == SQLITE_ROW )
|
||||
{
|
||||
const char *friendHandle = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) );
|
||||
if( friendHandle )
|
||||
{
|
||||
friend_list.push_back( Util::UTF8ToWide( friendHandle ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
|
||||
return friend_list;
|
||||
}
|
||||
|
||||
bool Database::SaveIgnore( const int64_t account_id, const std::wstring &ignore_handle )
|
||||
{
|
||||
if( account_id <= 0 || ignore_handle.empty() )
|
||||
{
|
||||
Log::Error( "Invalid parameters for SaveIgnore" );
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::SaveIgnore ];
|
||||
auto friendHandle = Util::WideToUTF8( ignore_handle );
|
||||
SQLiteTransaction tx( m_db );
|
||||
{
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
sqlite3_bind_text( stmt, 2, friendHandle.c_str(), -1, SQLITE_TRANSIENT );
|
||||
|
||||
if( sqlite3_step( stmt ) != SQLITE_DONE )
|
||||
{
|
||||
Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
return true;
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Database::RemoveIgnore( const int64_t account_id, const std::wstring &ignore_handle )
|
||||
{
|
||||
if( account_id <= 0 || ignore_handle.empty() )
|
||||
{
|
||||
Log::Error( "Invalid parameters for RemoveIgnore" );
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::RemoveIgnore ];
|
||||
auto ignoreHandle = Util::WideToUTF8( ignore_handle );
|
||||
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
sqlite3_bind_text( stmt, 2, ignoreHandle.c_str(), -1, SQLITE_TRANSIENT );
|
||||
|
||||
if( sqlite3_step( stmt ) != SQLITE_DONE )
|
||||
{
|
||||
Log::Error( "SQLite delete failed: {}", sqlite3_errmsg( m_db ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> Database::LoadIgnores( const int64_t account_id )
|
||||
{
|
||||
std::vector<std::wstring> ignore_list;
|
||||
|
||||
try
|
||||
{
|
||||
auto stmt = m_statements[ QueryID::LoadIgnoreList ];
|
||||
sqlite3_reset( stmt );
|
||||
sqlite3_clear_bindings( stmt );
|
||||
sqlite3_bind_int64( stmt, 1, account_id );
|
||||
|
||||
while( sqlite3_step( stmt ) == SQLITE_ROW )
|
||||
{
|
||||
const char *ignoreHandle = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) );
|
||||
if( ignoreHandle )
|
||||
{
|
||||
ignore_list.push_back( Util::UTF8ToWide( ignoreHandle ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( const std::exception &e )
|
||||
{
|
||||
Log::Error( "Database error: {}", std::string( e.what() ) );
|
||||
}
|
||||
|
||||
return ignore_list;
|
||||
}
|
||||
153
Source/Discovery Server/DiscoveryServer.cpp
Normal file
153
Source/Discovery Server/DiscoveryServer.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
// ╔═╗ ╦ ╦ ╔═╗ ╔╦╗ ╔═╗ ╦ ╔═╗ ╔╗╔ ╔═╗
|
||||
// ║ ╠═╣ ╠═╣ ║║║ ╠═╝ ║ ║ ║ ║║║ ╚═╗
|
||||
// ╚═╝ ╩ ╩ ╩ ╩ ╩ ╩ ╩ ╩ ╚═╝ ╝╚╝ ╚═╝
|
||||
// ╔╦╗╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦═╗╦ ╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗
|
||||
// ║║║╚═╗║ ║ ║╚╗╔╝║╣ ╠╦╝╚╦╝ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝
|
||||
// ═╩╝╩╚═╝╚═╝╚═╝ ╚╝ ╚═╝╩╚═ ╩ ╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═
|
||||
|
||||
#include "Discovery Server/DiscoveryServer.hpp"
|
||||
|
||||
#include "Common/ByteStream.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
DiscoveryServer::DiscoveryServer()
|
||||
{
|
||||
m_running = false;
|
||||
|
||||
m_socket = INVALID_SOCKET;
|
||||
m_recvBuffer.resize( 1024 );
|
||||
}
|
||||
|
||||
DiscoveryServer::~DiscoveryServer()
|
||||
{
|
||||
Log::Info( "Discovery Server stopped." );
|
||||
}
|
||||
|
||||
void DiscoveryServer::Start( std::string ip, int32_t port )
|
||||
{
|
||||
m_socket = socket( AF_INET, SOCK_DGRAM, 0 );
|
||||
|
||||
if( m_socket == INVALID_SOCKET )
|
||||
{
|
||||
Log::Error( "Failed to create socket." );
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr_in service{};
|
||||
service.sin_family = AF_INET;
|
||||
service.sin_port = htons( port );
|
||||
|
||||
if( ip == "0.0.0.0" )
|
||||
{
|
||||
service.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( InetPtonA( AF_INET, ip.c_str(), &service.sin_addr ) != 1 )
|
||||
{
|
||||
Log::Error( "Invalid IP address format: {}", ip );
|
||||
closesocket( m_socket );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( bind( m_socket, reinterpret_cast< SOCKADDR * >( &service ), sizeof( service ) ) == SOCKET_ERROR )
|
||||
{
|
||||
Log::Error( "Failed to bind socket." );
|
||||
closesocket( m_socket );
|
||||
return;
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
m_thread = std::thread( &DiscoveryServer::Run, this );
|
||||
|
||||
Log::Info( "Discovery Server started {}:{}", ip, port );
|
||||
}
|
||||
|
||||
void DiscoveryServer::Stop()
|
||||
{
|
||||
m_running = false;
|
||||
if( m_thread.joinable() )
|
||||
{
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryServer::Run()
|
||||
{
|
||||
sockaddr_in clientAddr;
|
||||
int clientAddrLen = sizeof( clientAddr );
|
||||
|
||||
while( m_running )
|
||||
{
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );
|
||||
|
||||
auto bytesReceived = recvfrom( m_socket, ( char * )m_recvBuffer.data(), 1024, 0, ( struct sockaddr * )&clientAddr, &clientAddrLen );
|
||||
|
||||
if( bytesReceived == SOCKET_ERROR || bytesReceived < 4 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcessPacket( &clientAddr, std::make_shared< ByteBuffer >( m_recvBuffer.data(), bytesReceived ) );
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryServer::ProcessPacket( sockaddr_in *clientAddr, sptr_byte_stream stream )
|
||||
{
|
||||
if( 0x20 != stream->read_u32() )
|
||||
return;
|
||||
|
||||
auto encryptedBytes = stream->read_bytes( 0x20 );
|
||||
auto decryptedBytes = RealmCrypt::decryptSymmetric( encryptedBytes );
|
||||
|
||||
std::wstring sessionId( 16, L'\0' );
|
||||
std::memcpy( sessionId.data(), decryptedBytes.data(), 0x20 );
|
||||
|
||||
// Validate the session ID is 16 characters long and not all 0's
|
||||
if( sessionId.size() != 16 || std::all_of( sessionId.begin(), sessionId.end(), []( wchar_t ch )
|
||||
{
|
||||
return ch == L'\0';
|
||||
} ) )
|
||||
{
|
||||
Log::Error( "Invalid session id." );
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the users remote IP and Port for discovery.
|
||||
char remoteIp[ INET_ADDRSTRLEN ];
|
||||
InetNtopA( AF_INET, &clientAddr->sin_addr, remoteIp, INET_ADDRSTRLEN );
|
||||
|
||||
uint16_t remotePort = ntohs( clientAddr->sin_port );
|
||||
|
||||
// Find the user associated with the session ID
|
||||
auto user = UserManager::Get().FindUserBySessionId( sessionId );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", sessionId );
|
||||
return;
|
||||
}
|
||||
|
||||
if( remoteIp != user->sock->remote_ip )
|
||||
{
|
||||
Log::Error( "Discovery Handshake from invalid IP!" );
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize our discovery information
|
||||
user->m_discoveryAddr = remoteIp;
|
||||
user->m_discoveryPort = remotePort;
|
||||
|
||||
Log::Debug( "Discovery Handshake from {}:{}", remoteIp, remotePort );
|
||||
|
||||
if( user->m_isHost )
|
||||
{
|
||||
GameSessionManager::Get().RequestOpen( user );
|
||||
}
|
||||
else
|
||||
{
|
||||
GameSessionManager::Get().RequestJoin( user );
|
||||
}
|
||||
}
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
422
Source/Lobby Server/LobbyServer.cpp
Normal file
422
Source/Lobby Server/LobbyServer.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
// ╔═╗ ╦ ╦ ╔═╗ ╔╦╗ ╔═╗ ╦ ╔═╗ ╔╗╔ ╔═╗
|
||||
// ║ ╠═╣ ╠═╣ ║║║ ╠═╝ ║ ║ ║ ║║║ ╚═╗
|
||||
// ╚═╝ ╩ ╩ ╩ ╩ ╩ ╩ ╩ ╩ ╚═╝ ╝╚╝ ╚═╝
|
||||
// ╦ ╔═╗╔╗ ╔╗ ╦ ╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗
|
||||
// ║ ║ ║╠╩╗╠╩╗╚╦╝ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝
|
||||
// ╩═╝╚═╝╚═╝╚═╝ ╩ ╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═
|
||||
|
||||
#include "Lobby Server/LobbyServer.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Network/Events.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
LobbyServer::LobbyServer()
|
||||
{
|
||||
m_running = false;
|
||||
m_conSocket.reset();
|
||||
m_rtaSocket.reset();
|
||||
|
||||
m_gatewaySockets.clear();
|
||||
|
||||
m_clientSockets.clear();
|
||||
m_recvBuffer.resize( 1024 );
|
||||
}
|
||||
|
||||
LobbyServer::~LobbyServer()
|
||||
{
|
||||
Log::Info( "Lobby Server stopped." );
|
||||
|
||||
for( auto &socket : m_clientSockets )
|
||||
{
|
||||
if( socket->fd != INVALID_SOCKET )
|
||||
{
|
||||
closesocket( socket->fd );
|
||||
}
|
||||
}
|
||||
|
||||
for( auto &socket : m_gatewaySockets )
|
||||
{
|
||||
if( socket->fd != INVALID_SOCKET )
|
||||
{
|
||||
closesocket( socket->fd );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LobbyServer::Start( std::string ip )
|
||||
{
|
||||
// Champions of Norrath Gateway Sockets
|
||||
for( int i = 0; i < 8; i++ )
|
||||
{
|
||||
auto gatewaySocket = OpenListenerSocket( ip, 40800 + i, RealmGameType::CHAMPIONS_OF_NORRATH );
|
||||
if( nullptr == gatewaySocket ) return;
|
||||
|
||||
gatewaySocket->flag.is_gateway = true;
|
||||
m_gatewaySockets.push_back( gatewaySocket );
|
||||
}
|
||||
|
||||
// Return to Arms Gateway Sockets
|
||||
for( int i = 0; i < 8; i++ )
|
||||
{
|
||||
auto gatewaySocket = OpenListenerSocket( ip, 40810 + i, RealmGameType::RETURN_TO_ARMS );
|
||||
if( nullptr == gatewaySocket ) return;
|
||||
|
||||
gatewaySocket->flag.is_gateway = true;
|
||||
m_gatewaySockets.push_back( gatewaySocket );
|
||||
}
|
||||
|
||||
m_conSocket = OpenListenerSocket( ip, Config::con_lobby_port, RealmGameType::CHAMPIONS_OF_NORRATH );
|
||||
m_rtaSocket = OpenListenerSocket( ip, Config::rta_lobby_port, RealmGameType::RETURN_TO_ARMS );
|
||||
|
||||
m_running = true;
|
||||
m_thread = std::thread( &LobbyServer::Run, this );
|
||||
|
||||
Log::Info( "Lobby Server started" );
|
||||
}
|
||||
|
||||
void LobbyServer::Stop()
|
||||
{
|
||||
Log::Info( "Stopping Lobby Server..." );
|
||||
|
||||
for( auto &client : m_clientSockets )
|
||||
{
|
||||
client->send( NotifyForcedLogout() );
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
if( m_thread.joinable() )
|
||||
{
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
sptr_socket LobbyServer::OpenListenerSocket( std::string ip, int32_t port, RealmGameType type )
|
||||
{
|
||||
SOCKET sock = ::WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED );
|
||||
if( sock == INVALID_SOCKET )
|
||||
{
|
||||
Log::Error( "WSASocket() failed on port {}", port );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sockaddr_in service{};
|
||||
service.sin_family = AF_INET;
|
||||
service.sin_port = htons( port );
|
||||
|
||||
if( ip == "0.0.0.0" )
|
||||
{
|
||||
service.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
else if( InetPtonA( AF_INET, ip.c_str(), &service.sin_addr) != 1 )
|
||||
{
|
||||
Log::Error( "Invalid IP address format: {}", ip );
|
||||
closesocket( sock );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( bind( sock, reinterpret_cast< SOCKADDR * >( &service ), sizeof( service ) ) == SOCKET_ERROR )
|
||||
{
|
||||
Log::Error( "bind() failed on port {}", port );
|
||||
closesocket( sock );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( listen( sock, SOMAXCONN ) == SOCKET_ERROR )
|
||||
{
|
||||
Log::Error( "listen() failed on port {}", port );
|
||||
closesocket( sock );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto realmSocket = std::make_shared< RealmSocket >();
|
||||
realmSocket->fd = sock;
|
||||
realmSocket->remote_ip = ip;
|
||||
realmSocket->remote_port = port;
|
||||
realmSocket->gameType = type;
|
||||
realmSocket->flag.is_listener = true;
|
||||
|
||||
Log::Info( "Socket Opened on {}:{}", ip, port );
|
||||
|
||||
return realmSocket;
|
||||
}
|
||||
|
||||
void LobbyServer::Run()
|
||||
{
|
||||
FD_SET readSet;
|
||||
FD_SET writeSet;
|
||||
|
||||
timeval timeout = { 0, 1000 };
|
||||
|
||||
while( m_running )
|
||||
{
|
||||
FD_ZERO( &readSet );
|
||||
FD_ZERO( &writeSet );
|
||||
|
||||
FD_SET( m_conSocket->fd, &readSet );
|
||||
FD_SET( m_rtaSocket->fd, &readSet );
|
||||
|
||||
for( const auto &sock : m_gatewaySockets )
|
||||
{
|
||||
if( sock != nullptr && sock->fd != INVALID_SOCKET )
|
||||
{
|
||||
FD_SET( sock->fd, &readSet );
|
||||
}
|
||||
}
|
||||
|
||||
CheckSocketProblem();
|
||||
|
||||
for( const auto &client : m_clientSockets )
|
||||
{
|
||||
FD_SET( client->fd, &readSet );
|
||||
FD_SET( client->fd, &writeSet );
|
||||
}
|
||||
|
||||
auto result = select( 0, &readSet, &writeSet, NULL, &timeout );
|
||||
|
||||
if( result == SOCKET_ERROR )
|
||||
{
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
for( auto &sock : m_gatewaySockets )
|
||||
{
|
||||
if( sock == nullptr || sock->fd == INVALID_SOCKET )
|
||||
continue;
|
||||
|
||||
if( FD_ISSET( sock->fd, &readSet ) )
|
||||
{
|
||||
AcceptConnection( sock );
|
||||
}
|
||||
}
|
||||
|
||||
if( FD_ISSET( m_conSocket->fd, &readSet ) )
|
||||
{
|
||||
AcceptConnection( m_conSocket );
|
||||
}
|
||||
|
||||
if( FD_ISSET( m_rtaSocket->fd, &readSet ) )
|
||||
{
|
||||
AcceptConnection( m_rtaSocket );
|
||||
}
|
||||
|
||||
for( auto &client : m_clientSockets )
|
||||
{
|
||||
if( FD_ISSET( client->fd, &readSet ) )
|
||||
{
|
||||
ReadSocket( client );
|
||||
}
|
||||
|
||||
if( FD_ISSET( client->fd, &writeSet ) )
|
||||
{
|
||||
WriteSocket( client );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LobbyServer::CheckSocketProblem()
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
for( auto it = m_clientSockets.begin(); it != m_clientSockets.end(); )
|
||||
{
|
||||
auto &socket = *it;
|
||||
auto elapsed = std::chrono::duration_cast< std::chrono::seconds >( now - socket->last_recv_time );
|
||||
|
||||
// Check for client timeouts
|
||||
if( elapsed.count() > 30 )
|
||||
{
|
||||
socket->flag.disconnected_forced = true;
|
||||
Log::Info( "[LOBBY] Client Timeout : ({})", socket->remote_ip );
|
||||
}
|
||||
|
||||
// Check if we're waiting to disconnect after sending buffered data
|
||||
if( socket->flag.disconnected_wait && socket->m_pendingWriteBuffer.empty() )
|
||||
{
|
||||
socket->flag.disconnected_forced = true;
|
||||
}
|
||||
|
||||
if( socket->flag.disconnected_forced )
|
||||
{
|
||||
UserManager::Get().RemoveUser( socket );
|
||||
Log::Info( "[LOBBY] Client Disconnected : ({})", socket->remote_ip );
|
||||
it = m_clientSockets.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LobbyServer::AcceptConnection( sptr_socket srcSocket )
|
||||
{
|
||||
sockaddr_in clientInfo{};
|
||||
int32_t addrSize = sizeof( clientInfo );
|
||||
|
||||
SOCKET clientSocket = accept( srcSocket->fd, ( SOCKADDR * )&clientInfo, &addrSize );
|
||||
if( clientSocket == INVALID_SOCKET )
|
||||
{
|
||||
Log::Error( "accept() failed" );
|
||||
return;
|
||||
}
|
||||
|
||||
auto new_socket = std::make_shared< RealmSocket >();
|
||||
auto gameType = srcSocket->gameType;
|
||||
|
||||
new_socket->fd = clientSocket;
|
||||
new_socket->remote_addr = clientInfo;
|
||||
new_socket->remote_ip = Util::IPFromAddr( clientInfo );
|
||||
new_socket->remote_port = ntohs( clientInfo.sin_port );
|
||||
new_socket->gameType = gameType;
|
||||
m_clientSockets.push_back( new_socket );
|
||||
|
||||
if( !srcSocket->flag.is_gateway )
|
||||
{
|
||||
UserManager::Get().CreateUser( new_socket, gameType );
|
||||
}
|
||||
|
||||
Log::Info( "New Client Connected : ({}) From Port {}", new_socket->remote_ip, srcSocket->remote_port );
|
||||
}
|
||||
|
||||
void LobbyServer::ReadSocket( sptr_socket socket )
|
||||
{
|
||||
if( socket->flag.disconnected_forced )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto bytesReceived = recv( socket->fd, reinterpret_cast< char * >( m_recvBuffer.data() ), static_cast< int >( m_recvBuffer.size() ), 0 );
|
||||
|
||||
if( bytesReceived == SOCKET_ERROR )
|
||||
{
|
||||
Log::Info( "Socket Error [{}].", WSAGetLastError() );
|
||||
socket->flag.disconnected_forced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if( bytesReceived == 0 )
|
||||
{
|
||||
socket->flag.disconnected_forced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
socket->last_recv_time = std::chrono::steady_clock::now();
|
||||
|
||||
// Append received data to socket's pending buffer
|
||||
socket->m_pendingReadBuffer.insert(
|
||||
socket->m_pendingReadBuffer.end(),
|
||||
m_recvBuffer.begin(),
|
||||
m_recvBuffer.begin() + bytesReceived
|
||||
);
|
||||
|
||||
// Process packets in the buffer
|
||||
while( socket->m_pendingReadBuffer.size() >= 4 )
|
||||
{
|
||||
int32_t packetSize = ntohl( *reinterpret_cast< const int32_t * >( &socket->m_pendingReadBuffer[ 0 ] ) );
|
||||
|
||||
if( packetSize <= 0 || packetSize > 2048 )
|
||||
{
|
||||
Log::Error( "Invalid packet size: {}. Disconnecting client.", packetSize );
|
||||
socket->flag.disconnected_wait = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if( socket->m_pendingReadBuffer.size() < static_cast< size_t >( packetSize ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto stream = std::make_shared< ByteBuffer >( socket->m_pendingReadBuffer.data() + 4, packetSize - 4 );
|
||||
|
||||
// Erase the packet from the buffer
|
||||
socket->m_pendingReadBuffer.erase(
|
||||
socket->m_pendingReadBuffer.begin(),
|
||||
socket->m_pendingReadBuffer.begin() + packetSize
|
||||
);
|
||||
|
||||
if( stream->m_buffer.size() == 0 ) break;
|
||||
|
||||
// Process the packet
|
||||
HandleRequest( socket, stream );
|
||||
}
|
||||
}
|
||||
|
||||
void LobbyServer::WriteSocket( sptr_socket socket )
|
||||
{
|
||||
if( socket->flag.disconnected_forced || socket->m_pendingWriteBuffer.empty() )
|
||||
return;
|
||||
|
||||
socket->last_send_time = std::chrono::steady_clock::now();
|
||||
|
||||
size_t totalBytesSent = 0;
|
||||
const size_t bufferSize = socket->m_pendingWriteBuffer.size();
|
||||
|
||||
while( totalBytesSent < bufferSize )
|
||||
{
|
||||
const size_t remaining = bufferSize - totalBytesSent;
|
||||
const int chunkSize = static_cast< int >( std::min<size_t>( remaining, 1024 ) );
|
||||
|
||||
int bytesSent = send(
|
||||
socket->fd,
|
||||
reinterpret_cast< const char * >( socket->m_pendingWriteBuffer.data() + totalBytesSent ),
|
||||
chunkSize,
|
||||
0
|
||||
);
|
||||
|
||||
if( bytesSent == SOCKET_ERROR )
|
||||
{
|
||||
const int err = WSAGetLastError();
|
||||
if( err == WSAEWOULDBLOCK )
|
||||
break;
|
||||
|
||||
Log::Error( "Send failed: {}", err );
|
||||
socket->flag.disconnected_wait = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if( bytesSent == 0 )
|
||||
{
|
||||
socket->flag.disconnected_wait = true;
|
||||
return;
|
||||
}
|
||||
|
||||
totalBytesSent += bytesSent;
|
||||
|
||||
if( bytesSent < chunkSize )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard< std::mutex > lock( socket->write_mutex );
|
||||
|
||||
socket->m_pendingWriteBuffer.erase(
|
||||
socket->m_pendingWriteBuffer.begin(),
|
||||
socket->m_pendingWriteBuffer.begin() + totalBytesSent
|
||||
);
|
||||
}
|
||||
|
||||
void LobbyServer::HandleRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
auto packetId = stream->read< uint16_t >();
|
||||
stream->set_position( 0 );
|
||||
|
||||
auto it = REQUEST_EVENT.find( packetId );
|
||||
if( it == REQUEST_EVENT.end() )
|
||||
{
|
||||
Log::Error( "[LOBBY] Unknown packet id : {}", packetId );
|
||||
Log::Packet( stream->m_buffer, stream->m_buffer.size(), false );
|
||||
return;
|
||||
}
|
||||
|
||||
auto request = it->second();
|
||||
|
||||
if( auto res = request->ProcessRequest( socket, stream ) )
|
||||
{
|
||||
socket->send( res );
|
||||
}
|
||||
}
|
||||
17
Source/Network/Event/NotifyClientDiscovered.cpp
Normal file
17
Source/Network/Event/NotifyClientDiscovered.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "Network\Event\NotifyClientDiscovered.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
|
||||
NotifyClientDiscovered::NotifyClientDiscovered( sptr_user user ) : GenericMessage( 0x40 )
|
||||
{
|
||||
this->m_clientIp = user->m_discoveryAddr;
|
||||
this->m_clientPort = user->m_discoveryPort;
|
||||
}
|
||||
|
||||
void NotifyClientDiscovered::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_sz_utf8( m_clientIp );
|
||||
out.write_u32( m_clientPort );
|
||||
}
|
||||
19
Source/Network/Event/NotifyClientDiscovered_RTA.cpp
Normal file
19
Source/Network/Event/NotifyClientDiscovered_RTA.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "Network\Event\NotifyClientDiscovered_RTA.hpp"
|
||||
|
||||
#include "Common/Constant.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
|
||||
NotifyClientDiscovered_RTA::NotifyClientDiscovered_RTA( sptr_user user ) : GenericMessage( 0x40 )
|
||||
{
|
||||
this->m_clientIp = user->m_discoveryAddr;
|
||||
this->m_clientPort = user->m_discoveryPort;
|
||||
}
|
||||
|
||||
void NotifyClientDiscovered_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_utf8( m_clientIp );
|
||||
out.write_u32( m_clientPort );
|
||||
}
|
||||
18
Source/Network/Event/NotifyClientReqConnect.cpp
Normal file
18
Source/Network/Event/NotifyClientReqConnect.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "NotifyClientReqConnect.h"
|
||||
|
||||
NotifyClientRequestConnect::NotifyClientRequestConnect( std::string clientIp, int32_t clientPort ) : GenericMessage( 0x3F )
|
||||
{
|
||||
this->m_clientIp = std::move( clientIp );
|
||||
this->m_clientPort = clientPort;
|
||||
}
|
||||
|
||||
ByteBuffer &NotifyClientRequestConnect::Serialize()
|
||||
{
|
||||
m_stream.write_u16( m_packetId );
|
||||
m_stream.write_u32( 0 );
|
||||
|
||||
m_stream.write_sz_utf8( m_clientIp );
|
||||
m_stream.write_u32( m_clientPort );
|
||||
|
||||
return m_stream;
|
||||
}
|
||||
17
Source/Network/Event/NotifyClientRequestConnect.cpp
Normal file
17
Source/Network/Event/NotifyClientRequestConnect.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "Network/Event/NotifyClientRequestConnect.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
|
||||
NotifyClientRequestConnect::NotifyClientRequestConnect( sptr_user user ) : GenericMessage( 0x3F )
|
||||
{
|
||||
this->m_clientIp = user->m_discoveryAddr;
|
||||
this->m_clientPort = user->m_discoveryPort;
|
||||
}
|
||||
|
||||
void NotifyClientRequestConnect::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_sz_utf8( m_clientIp );
|
||||
out.write_u32( m_clientPort );
|
||||
}
|
||||
25
Source/Network/Event/NotifyClientRequestConnect_RTA.cpp
Normal file
25
Source/Network/Event/NotifyClientRequestConnect_RTA.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "Network\Event\NotifyClientRequestConnect_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUser.hpp"
|
||||
|
||||
NotifyClientRequestConnect_RTA::NotifyClientRequestConnect_RTA( sptr_user user ) : GenericMessage( 0x65 )
|
||||
{
|
||||
this->m_remoteAddr = user->m_discoveryAddr;
|
||||
this->m_remotePort = user->m_discoveryPort;
|
||||
|
||||
this->m_localAddr = user->m_localAddr;
|
||||
this->m_localPort = user->m_localPort;
|
||||
|
||||
}
|
||||
|
||||
void NotifyClientRequestConnect_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_utf8( this->m_localAddr );
|
||||
out.write_u32( this->m_localPort );
|
||||
|
||||
out.write_utf8( this->m_remoteAddr );
|
||||
out.write_u32( this->m_remotePort );
|
||||
}
|
||||
12
Source/Network/Event/NotifyForcedLogout.cpp
Normal file
12
Source/Network/Event/NotifyForcedLogout.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "Network/Event/NotifyForcedLogout.hpp"
|
||||
|
||||
NotifyForcedLogout::NotifyForcedLogout() : GenericMessage( 0x41 )
|
||||
{
|
||||
}
|
||||
|
||||
void NotifyForcedLogout::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
out.write_u32( 0 );
|
||||
}
|
||||
16
Source/Network/Event/NotifyFriendStatus.cpp
Normal file
16
Source/Network/Event/NotifyFriendStatus.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "Network\Event\NotifyFriendStatus.hpp"
|
||||
|
||||
NotifyFriendStatus::NotifyFriendStatus( std::wstring handle, bool status ) : GenericMessage( 0x2F )
|
||||
{
|
||||
m_handle = handle;
|
||||
m_status = status;
|
||||
}
|
||||
|
||||
void NotifyFriendStatus::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_utf16( m_handle );
|
||||
out.write_u8( m_status ? 1 : 0 );
|
||||
}
|
||||
22
Source/Network/Event/NotifyGameDiscovered.cpp
Normal file
22
Source/Network/Event/NotifyGameDiscovered.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "Network/Event/NotifyGameDiscovered.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
|
||||
NotifyGameDiscovered::NotifyGameDiscovered( sptr_user user ) : GenericMessage( 0x3E )
|
||||
{
|
||||
this->m_clientIp = user->m_discoveryAddr;
|
||||
this->m_clientPort = user->m_discoveryPort;
|
||||
this->m_clientType = user->m_gameType;
|
||||
}
|
||||
|
||||
void NotifyGameDiscovered::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
if( m_clientType == RealmGameType::RETURN_TO_ARMS )
|
||||
out.write_utf8( m_clientIp );
|
||||
else
|
||||
out.write_sz_utf8( m_clientIp );
|
||||
|
||||
out.write_u32( m_clientPort );
|
||||
}
|
||||
16
Source/Network/Event/NotifyInstantMessage.cpp
Normal file
16
Source/Network/Event/NotifyInstantMessage.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "Network\Event\NotifyInstantMessage.hpp"
|
||||
|
||||
NotifyInstantMessage::NotifyInstantMessage( std::wstring chatHandle, std::wstring message ) : GenericMessage( 0x30 )
|
||||
{
|
||||
m_chatHandle = chatHandle;
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
void NotifyInstantMessage::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_utf16( m_chatHandle );
|
||||
out.write_utf16( m_message );
|
||||
}
|
||||
19
Source/Network/Event/NotifyReserveUserSlot_RTA.cpp
Normal file
19
Source/Network/Event/NotifyReserveUserSlot_RTA.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "NotifyReserveUserSlot_RTA.h"
|
||||
|
||||
NotifyReserveUserSlot_RTA::NotifyReserveUserSlot_RTA( int32_t memberId, std::string discoveryAddr, int32_t discoveryPort ) : GenericMessage( 0x51 )
|
||||
{
|
||||
this->m_discoveryAddr = discoveryAddr;
|
||||
this->m_port = discoveryPort;
|
||||
this->m_memberId = memberId;
|
||||
}
|
||||
|
||||
// This notification is nullsub.
|
||||
void NotifyReserveUserSlot_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_u32( 0 );
|
||||
out.write_utf8( "" );
|
||||
out.write_u32( 0 );
|
||||
}
|
||||
18
Source/Network/Event/NotifyRoomMessage.cpp
Normal file
18
Source/Network/Event/NotifyRoomMessage.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "Network\Event\NotifyRoomMessage.hpp"
|
||||
|
||||
NotifyRoomMessage::NotifyRoomMessage( std::wstring roomName, std::wstring chatHandle, std::wstring message ) : GenericMessage( 0x3D )
|
||||
{
|
||||
m_roomName = roomName;
|
||||
m_chatHandle = chatHandle;
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
void NotifyRoomMessage::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_utf16( m_roomName );
|
||||
out.write_utf16( m_chatHandle );
|
||||
out.write_utf16( m_message );
|
||||
}
|
||||
49
Source/Network/Event/Notify_4C.cpp
Normal file
49
Source/Network/Event/Notify_4C.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "Notify_4C.h"
|
||||
|
||||
Notify_4C::Notify_4C() : GenericMessage( 0x4C )
|
||||
{
|
||||
}
|
||||
|
||||
void Notify_4C::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_u32( 0 ); // Unknown
|
||||
|
||||
out.write_u32( 1 ); // Unknown
|
||||
{
|
||||
out.write_utf16( L"Dummy 1" );
|
||||
}
|
||||
|
||||
out.write_u32( 1 ); // Unknown
|
||||
{
|
||||
out.write_u32( 0 );
|
||||
// Blob
|
||||
}
|
||||
|
||||
out.write_u32( 1 ); // Unknown
|
||||
{
|
||||
out.write_u32( 0 );
|
||||
}
|
||||
|
||||
out.write_u16( 0 ); // Unknown
|
||||
|
||||
out.write_u32( 1 ); // Unknown
|
||||
{
|
||||
out.write_u16( 0 );
|
||||
out.write_u32( 0 );
|
||||
out.write_utf16( L"Dummy 2" );
|
||||
out.write_u32( 0 );
|
||||
out.write_u32( 0 );
|
||||
out.write_u32( 0 );
|
||||
out.write_u16( 1 );
|
||||
}
|
||||
|
||||
out.write_u32( 0 ); // Unknown
|
||||
|
||||
out.write_utf8( "127.0.0.1" );
|
||||
|
||||
out.write_u32( 0 ); // Unknown
|
||||
out.write_u32( 0 ); // Unknown
|
||||
}
|
||||
22
Source/Network/Event/Notify_65.cpp
Normal file
22
Source/Network/Event/Notify_65.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "Notify_65.h"
|
||||
|
||||
NotifyClientRequestsConnect2::NotifyClientRequestsConnect2( std::string discoveryAddr, std::string localAddr, int32_t port ) : GenericMessage( 0x65 )
|
||||
{
|
||||
m_discoveryAddr = std::move( discoveryAddr );
|
||||
m_localAddr = std::move( localAddr );
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
ByteBuffer &NotifyClientRequestsConnect2::Serialize()
|
||||
{
|
||||
m_stream.write_u16( m_packetId );
|
||||
m_stream.write_u32( 0 );
|
||||
|
||||
m_stream.write_utf8( m_discoveryAddr );
|
||||
m_stream.write_u32( m_port );
|
||||
|
||||
m_stream.write_utf8( m_localAddr );
|
||||
m_stream.write_u32( m_port );
|
||||
|
||||
return m_stream;
|
||||
}
|
||||
61
Source/Network/Event/RequestAddFriend.cpp
Normal file
61
Source/Network/Event/RequestAddFriend.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "Network/Event/RequestAddFriend.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestAddFriend::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_chatHandle = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestAddFriend::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultAddFriend >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( user->IsFriend( m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultAddFriend >( this, FRIEND_DUPLICATE );
|
||||
}
|
||||
|
||||
auto targetUser = UserManager::Get().FindUserByChatHandle( m_chatHandle );
|
||||
if( targetUser == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultAddFriend >( this, FRIEND_INVALID );
|
||||
}
|
||||
|
||||
if( targetUser->IsIgnored( user->m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultAddFriend >( this, FRIEND_IGNORING );
|
||||
}
|
||||
|
||||
if( !Database::Get().SaveFriend( user->m_accountId, targetUser->m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultAddFriend >( this, DATABASE_ERROR );
|
||||
}
|
||||
|
||||
user->m_friendList.push_back( targetUser->m_chatHandle );
|
||||
|
||||
return std::make_shared< ResultAddFriend >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultAddFriend::ResultAddFriend( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultAddFriend::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
61
Source/Network/Event/RequestAddIgnore.cpp
Normal file
61
Source/Network/Event/RequestAddIgnore.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "Network/Event/RequestAddIgnore.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestAddIgnore::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_chatHandle = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestAddIgnore::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultAddIgnore >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( user->IsFriend( m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultAddIgnore >( this, IGNORE_FRIEND );
|
||||
}
|
||||
|
||||
if( user->IsIgnored( m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultAddIgnore >( this, IGNORE_DUPLICATE );
|
||||
}
|
||||
|
||||
auto targetUser = UserManager::Get().FindUserByChatHandle( m_chatHandle );
|
||||
if( targetUser == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultAddIgnore >( this, IGNORE_INVALID );
|
||||
}
|
||||
|
||||
if( !Database::Get().SaveIgnore( user->m_accountId, targetUser->m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultAddIgnore >( this, DATABASE_ERROR );
|
||||
}
|
||||
|
||||
user->m_ignoreList.push_back( targetUser->m_chatHandle );
|
||||
|
||||
return std::make_shared< ResultAddIgnore >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultAddIgnore::ResultAddIgnore( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultAddIgnore::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
45
Source/Network/Event/RequestAppendCharacterData.cpp
Normal file
45
Source/Network/Event/RequestAppendCharacterData.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "Network/Event/RequestAppendCharacterData.hpp"
|
||||
|
||||
#include "Game/CharacterSaveManager.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestAppendCharacterData::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
auto sessionId = stream->read_encrypted_utf16();
|
||||
|
||||
auto length = stream->read_u32();
|
||||
m_data = stream->read_bytes( length );
|
||||
m_endOfData = stream->read_u32();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestAppendCharacterData::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultAppendCharacterData >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
CharacterSaveManager::Get().AppendSaveData( user->m_sessionId, m_data, m_endOfData );
|
||||
|
||||
return std::make_shared< ResultAppendCharacterData >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultAppendCharacterData::ResultAppendCharacterData( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultAppendCharacterData::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
47
Source/Network/Event/RequestCancelGame.cpp
Normal file
47
Source/Network/Event/RequestCancelGame.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "Network/Event/RequestCancelGame.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestCancelGame::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCancelGame::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCancelGame >( this );
|
||||
}
|
||||
|
||||
if( user->m_gameType != RealmGameType::CHAMPIONS_OF_NORRATH )
|
||||
{
|
||||
return std::make_shared< ResultCancelGame >( this );
|
||||
}
|
||||
|
||||
if( !GameSessionManager::Get().RequestCancel( user ) )
|
||||
{
|
||||
Log::Error( "Failed to cancel game session for user [{}]", user->m_sessionId );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultCancelGame >( this );
|
||||
}
|
||||
|
||||
ResultCancelGame::ResultCancelGame( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ResultCancelGame::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
}
|
||||
61
Source/Network/Event/RequestCancelGame_RTA.cpp
Normal file
61
Source/Network/Event/RequestCancelGame_RTA.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "Network/Event/RequestCancelGame_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestCancelGame_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCancelGame_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCancelGame_RTA >( this );
|
||||
}
|
||||
|
||||
if( user->m_gameType != RealmGameType::RETURN_TO_ARMS )
|
||||
{
|
||||
return std::make_shared< ResultCancelGame_RTA >( this );
|
||||
}
|
||||
|
||||
const auto &gameSession = GameSessionManager::Get().FindGame( user->m_gameId, user->m_gameType );
|
||||
if( gameSession == nullptr )
|
||||
{
|
||||
Log::Error( "Game session not found for user [{}]", user->m_sessionId );
|
||||
return std::make_shared< ResultCancelGame_RTA >( this );
|
||||
}
|
||||
|
||||
if( !GameSessionManager::Get().RequestCancel( user ) )
|
||||
{
|
||||
Log::Error( "Failed to cancel game session for user [{}]", user->m_sessionId );
|
||||
}
|
||||
|
||||
if( !ChatRoomManager::Get().LeaveRoom( user, user->m_privateRoomId ) )
|
||||
{
|
||||
Log::Error( "Failed to leave private chat room for user [{}]", user->m_username );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultCancelGame_RTA >( this );
|
||||
}
|
||||
|
||||
ResultCancelGame_RTA::ResultCancelGame_RTA( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ResultCancelGame_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
}
|
||||
97
Source/Network/Event/RequestCreateAccount.cpp
Normal file
97
Source/Network/Event/RequestCreateAccount.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "Network/Event/RequestCreateAccount.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Common/Constant.hpp"
|
||||
#include "Crypto/PasswordHash.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestCreateAccount::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_username = stream->read_encrypted_utf16();
|
||||
m_password = stream->read_encrypted_utf16();
|
||||
m_emailAddress = stream->read_encrypted_utf16();
|
||||
m_dateOfBirth = stream->read_encrypted_utf16();
|
||||
m_chatHandle = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
bool RequestCreateAccount::VerifyUserData()
|
||||
{
|
||||
if( m_username.empty() || m_password.empty() || m_emailAddress.empty() || m_dateOfBirth.empty() || m_chatHandle.empty() )
|
||||
{
|
||||
Log::Error( "RequestCreateAccount::VerifyUserData() - Missing required fields for account creation." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_username.length() < 3 || m_username.length() > 20 )
|
||||
{
|
||||
Log::Error( "RequestCreateAccount::VerifyUserData() - Username must be between 3 and 20 characters." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_password.length() < 6 || m_password.length() > 32 )
|
||||
{
|
||||
Log::Error( "RequestCreateAccount::VerifyUserData() - Password must be between 6 and 32 characters." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCreateAccount::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( nullptr == user || user->m_gameType != RealmGameType::RETURN_TO_ARMS )
|
||||
{
|
||||
return std::make_shared< ResultCreateAccount >( this, ERROR_FATAL, L"" );
|
||||
}
|
||||
|
||||
if( m_username.empty() || m_password.empty() || m_emailAddress.empty() || m_dateOfBirth.empty() || m_chatHandle.empty() )
|
||||
{
|
||||
Log::Error( "RequestCreateAccount::ProcessRequest() - Missing required fields for account creation." );
|
||||
return std::make_shared< ResultCreateAccount >( this, ERROR_FATAL, L"" );
|
||||
}
|
||||
|
||||
auto result = Database::Get().CreateNewAccount
|
||||
(
|
||||
Util::WideToUTF8( m_username ),
|
||||
Util::WideToUTF8( m_password ),
|
||||
Util::WideToUTF8( m_emailAddress ),
|
||||
Util::WideToUTF8( m_dateOfBirth ),
|
||||
Util::WideToUTF8( m_chatHandle )
|
||||
);
|
||||
|
||||
if( !result )
|
||||
{
|
||||
Log::Error( "RequestCreateAccount::ProcessRequest() - Failed to create account for user: {}", m_username );
|
||||
return std::make_shared< ResultCreateAccount >( this, ERROR_FATAL, L"" );
|
||||
}
|
||||
|
||||
user->m_isLoggedIn = true;
|
||||
user->m_sessionId = UserManager::Get().GenerateSessionId();
|
||||
user->m_accountId = result;
|
||||
user->m_username = m_username;
|
||||
user->m_chatHandle = m_chatHandle;
|
||||
|
||||
return std::make_shared< ResultCreateAccount >( this, SUCCESS, user->m_sessionId );
|
||||
}
|
||||
|
||||
ResultCreateAccount::ResultCreateAccount( GenericRequest *request, int32_t reply, std::wstring sessionId ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_sessionId = sessionId;
|
||||
}
|
||||
|
||||
void ResultCreateAccount::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_encrypted_utf16( m_sessionId );
|
||||
}
|
||||
64
Source/Network/Event/RequestCreateNewCharacter_RTA.cpp
Normal file
64
Source/Network/Event/RequestCreateNewCharacter_RTA.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "Network/Event/RequestCreateNewCharacter_RTA.hpp"
|
||||
|
||||
#include "Database/Database.hpp"
|
||||
#include "Game/CharacterSaveManager.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Game/RealmCharacter.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestCreateNewCharacter_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
|
||||
auto a = stream->read_u32();
|
||||
auto b = stream->read_u32();
|
||||
|
||||
m_metaData.Deserialize( stream );
|
||||
|
||||
auto characterDataSize = stream->read_u32();
|
||||
m_characterData = stream->read_bytes( characterDataSize );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCreateNewCharacter_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCreateNewCharacter_RTA >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( user->m_gameType != RealmGameType::RETURN_TO_ARMS )
|
||||
{
|
||||
Log::Error( "Invalid game type for CreateNewCharacter_RTA request! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCreateNewCharacter_RTA >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
auto &saveManager = CharacterSaveManager::Get();
|
||||
if( !saveManager.BeginSaveTask( user, user, 0, m_metaData, CharacterSaveType::NEW_CHARACTER ) )
|
||||
{
|
||||
Log::Error( "Failed to begin save task for new character! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCreateNewCharacter_RTA >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
saveManager.AppendSaveData( user->m_sessionId, m_characterData, false );
|
||||
|
||||
return std::make_shared< ResultCreateNewCharacter_RTA >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultCreateNewCharacter_RTA::ResultCreateNewCharacter_RTA( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultCreateNewCharacter_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
61
Source/Network/Event/RequestCreatePrivateGame.cpp
Normal file
61
Source/Network/Event/RequestCreatePrivateGame.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "Network/Event/RequestCreatePrivateGame.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestCreatePrivateGame::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_gameName = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCreatePrivateGame::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCreatePrivateGame >( this, FATAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
if( nullptr != GameSessionManager::Get().FindGame( m_gameName, user->m_gameType ) )
|
||||
{
|
||||
Log::Error( "Game name is already in use! [{}]", m_gameName );
|
||||
return std::make_shared< ResultCreatePrivateGame >( this, GAME_NAME_IN_USE, "", 0 );
|
||||
}
|
||||
|
||||
auto result = GameSessionManager::Get().CreateGameSession_CON( user, L"", m_gameName, L"", true);
|
||||
|
||||
if( !result )
|
||||
{
|
||||
Log::Error( "RequestCreatePrivateGame::ProcessRequest() - Failed to create private game session!" );
|
||||
return std::make_shared< ResultCreatePrivateGame >( this, GENERAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
Log::Info( "[{}] Create Private Game: {}", m_sessionId, m_gameName );
|
||||
|
||||
return std::make_shared< ResultCreatePrivateGame >( this, SUCCESS, Config::service_ip, Config::discovery_port );
|
||||
}
|
||||
|
||||
ResultCreatePrivateGame::ResultCreatePrivateGame( GenericRequest *request, int32_t reply, std::string discoveryIp, int32_t discoveryPort ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_discoveryIp = discoveryIp;
|
||||
m_discoveryPort = discoveryPort;
|
||||
}
|
||||
|
||||
void ResultCreatePrivateGame::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_sz_utf8( m_discoveryIp );
|
||||
out.write( m_discoveryPort );
|
||||
}
|
||||
59
Source/Network/Event/RequestCreatePrivateGame_RTA.cpp
Normal file
59
Source/Network/Event/RequestCreatePrivateGame_RTA.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "Network/Event/RequestCreatePrivateGame_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestCreatePrivateGame_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_gameName = stream->read_utf16();
|
||||
m_localAddr = Util::WideToUTF8( stream->read_utf16() );
|
||||
m_localPort = stream->read_u32();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCreatePrivateGame_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCreatePrivateGame_RTA >( this, FATAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
auto result = GameSessionManager::Get().CreateGameSession_RTA( user, L"", m_gameName, {}, true);
|
||||
if( !result )
|
||||
{
|
||||
Log::Error( "RequestCreatePrivateGame2::ProcessRequest() - Failed to create private game session!" );
|
||||
return std::make_shared< ResultCreatePrivateGame_RTA >( this, GENERAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
user->m_localAddr = m_localAddr;
|
||||
user->m_localPort = m_localPort;
|
||||
|
||||
Log::Info( "[{}] Create Private Game: {}", m_sessionId, m_gameName );
|
||||
|
||||
return std::make_shared< ResultCreatePrivateGame_RTA >( this, SUCCESS, Config::service_ip, Config::discovery_port );
|
||||
}
|
||||
|
||||
ResultCreatePrivateGame_RTA::ResultCreatePrivateGame_RTA( GenericRequest *request, int32_t reply, std::string discoveryIp, int32_t discoveryPort ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_discoveryIp = discoveryIp;
|
||||
m_discoveryPort = discoveryPort;
|
||||
}
|
||||
|
||||
void ResultCreatePrivateGame_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_utf8( m_discoveryIp );
|
||||
out.write( m_discoveryPort );
|
||||
}
|
||||
45
Source/Network/Event/RequestCreatePrivateRoom.cpp
Normal file
45
Source/Network/Event/RequestCreatePrivateRoom.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "Network/Event/RequestCreatePrivateRoom.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestCreatePrivateRoom::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_roomName = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCreatePrivateRoom::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultCreatePrivateRoom >( this );
|
||||
}
|
||||
|
||||
auto result = ChatRoomManager::Get().CreateGameChatSession( user, m_roomName );
|
||||
|
||||
if( !result )
|
||||
{
|
||||
Log::Error( "RequestCreatePrivateRoom::ProcessRequest() - Failed to create private room!" );
|
||||
return std::make_shared< ResultCreatePrivateRoom >( this );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultCreatePrivateRoom >( this );
|
||||
}
|
||||
|
||||
ResultCreatePrivateRoom::ResultCreatePrivateRoom( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
}
|
||||
|
||||
void ResultCreatePrivateRoom::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
}
|
||||
96
Source/Network/Event/RequestCreatePublicGame.cpp
Normal file
96
Source/Network/Event/RequestCreatePublicGame.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "Network/Event/RequestCreatePublicGame.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
// Request
|
||||
void RequestCreatePublicGame::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
|
||||
// Some kind of match attributes
|
||||
auto unknown_a = stream->read_u16();
|
||||
auto unknown_b = stream->read_u32();
|
||||
auto unknown_c = stream->read_u32();
|
||||
auto unknown_d = stream->read_u32();
|
||||
|
||||
m_gameInfo = stream->read_utf16();
|
||||
|
||||
auto [name, stage] = ParseNameAndStage( m_gameInfo );
|
||||
|
||||
m_gameName = name;
|
||||
m_stageName = stage;
|
||||
}
|
||||
|
||||
std::tuple<std::wstring, std::wstring> RequestCreatePublicGame::ParseNameAndStage( const std::wstring &gameInfo )
|
||||
{
|
||||
const size_t open = gameInfo.find( L'[' );
|
||||
const size_t close = gameInfo.find( L']' );
|
||||
|
||||
if( open == std::wstring::npos || close == std::wstring::npos || close < open )
|
||||
return { L"", L"" };
|
||||
|
||||
std::wstring name = gameInfo.substr( 0, open );
|
||||
std::wstring stage = gameInfo.substr( open + 1, close - open - 1 );
|
||||
|
||||
if( !name.empty() && iswspace( name.back() ) )
|
||||
name.pop_back();
|
||||
|
||||
return { name, stage };
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCreatePublicGame::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCreatePublicGame >( this, FATAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
if( nullptr != GameSessionManager::Get().FindGame( m_gameName, user->m_gameType ) )
|
||||
{
|
||||
Log::Error( "Game name is already in use! [{}]", m_gameName );
|
||||
return std::make_shared< ResultCreatePublicGame >( this, GAME_NAME_IN_USE, "", 0 );
|
||||
}
|
||||
|
||||
auto result = GameSessionManager::Get().CreateGameSession_CON( user, m_gameInfo, m_gameName, m_stageName, false );
|
||||
|
||||
if( !result )
|
||||
{
|
||||
Log::Error( "RequestCreatePublicGame::ProcessRequest() - Failed to create public game session!" );
|
||||
return std::make_shared< ResultCreatePublicGame >( this, GENERAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
Log::Info( "[{}] Create Public Game: {}", m_sessionId, m_gameInfo );
|
||||
|
||||
user->m_isHost = true;
|
||||
user->m_discoveryAddr = "";
|
||||
user->m_discoveryPort = 0;
|
||||
|
||||
return std::make_shared< ResultCreatePublicGame >( this, SUCCESS, Config::service_ip, Config::discovery_port );
|
||||
}
|
||||
|
||||
// Result
|
||||
ResultCreatePublicGame::ResultCreatePublicGame( GenericRequest *request, int32_t reply, std::string discoveryIp, int32_t discoveryPort ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_discoveryIp = discoveryIp;
|
||||
m_discoveryPort = discoveryPort;
|
||||
}
|
||||
|
||||
void ResultCreatePublicGame::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_sz_utf8( m_discoveryIp );
|
||||
out.write( m_discoveryPort );
|
||||
}
|
||||
114
Source/Network/Event/RequestCreatePublicGame_RTA.cpp
Normal file
114
Source/Network/Event/RequestCreatePublicGame_RTA.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "Network/Event/RequestCreatePublicGame_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestCreatePublicGame_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
|
||||
auto unknown_a = stream->read_u16();
|
||||
auto unknown_b = stream->read_u32();
|
||||
auto unknown_c = stream->read_u32();
|
||||
auto unknown_d = stream->read_u32();
|
||||
|
||||
m_gameInfo = stream->read_utf16();
|
||||
|
||||
auto unknown_e = stream->read_u32();
|
||||
auto unknown_f = stream->read_u32();
|
||||
|
||||
m_localAddr = Util::WideToUTF8( stream->read_utf16() );
|
||||
m_localPort = stream->read_u32();
|
||||
|
||||
if( !ParseGameInfo() )
|
||||
{
|
||||
Log::Error( "Failed to parse game info: {}", m_gameInfo );
|
||||
}
|
||||
}
|
||||
|
||||
bool RequestCreatePublicGame_RTA::ParseGameInfo()
|
||||
{
|
||||
if( m_gameInfo.empty() )
|
||||
{
|
||||
Log::Error( "Game info is empty!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t pipePos = m_gameInfo.find( L'|' );
|
||||
if( pipePos == std::wstring::npos )
|
||||
{
|
||||
Log::Error( "Invalid game info format!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring numbersPart = m_gameInfo.substr( 0, pipePos );
|
||||
m_gameName = m_gameInfo.substr( pipePos + 1 );
|
||||
std::wstringstream ss( numbersPart );
|
||||
std::wstring numStr;
|
||||
std::array<int8_t, 5> fields = { 0, 0, 0, 0, 0 };
|
||||
|
||||
for( size_t i = 0; i < fields.size(); ++i )
|
||||
{
|
||||
if( !std::getline( ss, numStr, L',' ) )
|
||||
return false;
|
||||
try
|
||||
{
|
||||
fields[ i ] = std::stoi( numStr );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sptr_generic_response RequestCreatePublicGame_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultCreatePublicGame_RTA >( this, GENERAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
auto result = GameSessionManager::Get().CreateGameSession_RTA( user, m_gameInfo, m_gameName, m_attributes, false );
|
||||
if( !result )
|
||||
{
|
||||
Log::Error( "RequestCreatePublicGame::ProcessRequest() - Failed to create public game session!" );
|
||||
return std::make_shared< ResultCreatePublicGame_RTA >( this, GENERAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
user->m_localAddr = m_localAddr;
|
||||
user->m_localPort = m_localPort;
|
||||
|
||||
Log::Info( "[{}] Create Public Game: {}", user->m_username, m_gameInfo );
|
||||
|
||||
return std::make_shared< ResultCreatePublicGame_RTA >( this, SUCCESS, Config::service_ip, Config::discovery_port );
|
||||
}
|
||||
|
||||
// Result
|
||||
ResultCreatePublicGame_RTA::ResultCreatePublicGame_RTA( GenericRequest *request, int32_t reply, std::string discoveryIp, int32_t discoveryPort ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_discoveryIp = discoveryIp;
|
||||
m_discoveryPort = discoveryPort;
|
||||
}
|
||||
|
||||
void ResultCreatePublicGame_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_utf8( m_discoveryIp );
|
||||
out.write( m_discoveryPort );
|
||||
}
|
||||
58
Source/Network/Event/RequestDoClientDiscovery.cpp
Normal file
58
Source/Network/Event/RequestDoClientDiscovery.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "Network/Event/RequestDoClientDiscovery.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestDoClientDiscovery::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_gameId = stream->read_u32();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestDoClientDiscovery::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultDoClientDiscovery >( this, DISCOVERY_REPLY::FATAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
auto session = GameSessionManager::Get().FindGame( m_gameId, user->m_gameType );
|
||||
if( session == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultDoClientDiscovery >( this, DISCOVERY_REPLY::FATAL_ERROR, "", 0 );
|
||||
}
|
||||
|
||||
if( !session->IsJoinable( user ) )
|
||||
{
|
||||
return std::make_shared< ResultDoClientDiscovery >( this, DISCOVERY_REPLY::GAME_FULL, "", 0 );
|
||||
}
|
||||
|
||||
user->m_gameId = m_gameId;
|
||||
user->m_isHost = false;
|
||||
|
||||
return std::make_shared< ResultDoClientDiscovery >( this, DISCOVERY_REPLY::SUCCESS, Config::service_ip, Config::discovery_port );
|
||||
}
|
||||
|
||||
ResultDoClientDiscovery::ResultDoClientDiscovery( GenericRequest *request, int32_t reply, std::string ip, int32_t port ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_discoveryIP = ip;
|
||||
m_discoveryPort = port;
|
||||
}
|
||||
|
||||
void ResultDoClientDiscovery::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_sz_utf8( m_discoveryIP );
|
||||
out.write( m_discoveryPort );
|
||||
}
|
||||
59
Source/Network/Event/RequestDoClientDiscovery_RTA.cpp
Normal file
59
Source/Network/Event/RequestDoClientDiscovery_RTA.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "Network/Event/RequestDoClientDiscovery_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestDoClientDiscovery_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_gameId = stream->read_u32();
|
||||
m_localAddr = stream->read_utf16();
|
||||
m_localPort = stream->read_u32();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestDoClientDiscovery_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultDoClientDiscovery_RTA >( this, TIMEOUT );
|
||||
}
|
||||
|
||||
auto session = GameSessionManager::Get().FindGame( m_gameId, user->m_gameType );
|
||||
if( session == nullptr )
|
||||
{
|
||||
Log::Error( "Game session not found! [{}]", m_gameId );
|
||||
return std::make_shared< ResultDoClientDiscovery_RTA >( this, NOT_FOUND );
|
||||
}
|
||||
|
||||
user->m_isHost = false;
|
||||
user->m_gameId = session->m_gameId;
|
||||
user->m_localAddr = Util::WideToUTF8(m_localAddr);
|
||||
user->m_localPort = m_localPort;
|
||||
|
||||
return std::make_shared< ResultDoClientDiscovery_RTA >( this, SUCCESS, Config::service_ip, Config::discovery_port );
|
||||
}
|
||||
|
||||
ResultDoClientDiscovery_RTA::ResultDoClientDiscovery_RTA( GenericRequest *request, int32_t reply, std::string discoveryIp, int32_t discoveryPort ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_discoveryIp = discoveryIp;
|
||||
m_discoveryPort = discoveryPort;
|
||||
}
|
||||
|
||||
void ResultDoClientDiscovery_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_utf8( m_discoveryIp );
|
||||
out.write_u32( m_discoveryPort );
|
||||
}
|
||||
85
Source/Network/Event/RequestEnterRoom.cpp
Normal file
85
Source/Network/Event/RequestEnterRoom.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "Network/Event/RequestEnterRoom.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestEnterRoom::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
auto sessionId = stream->read_encrypted_utf16();
|
||||
m_roomName = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestEnterRoom::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
const auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( !user )
|
||||
{
|
||||
Log::Error( "User not found for socket!" );
|
||||
return std::make_shared< ResultEnterRoom >( this, GENERAL_ERROR, nullptr );
|
||||
}
|
||||
|
||||
if( !ChatRoomManager::Get().JoinRoom( user, m_roomName ) )
|
||||
{
|
||||
Log::Error( "Failed to join room [{}] for user [{}]", m_roomName, user->m_username );
|
||||
return std::make_shared< ResultEnterRoom >( this, GENERAL_ERROR, nullptr );
|
||||
}
|
||||
|
||||
const auto room = ChatRoomManager::Get().FindRoom( m_roomName );
|
||||
if( !room )
|
||||
{
|
||||
Log::Error( "Chat room [{}] not found after joining", m_roomName );
|
||||
return std::make_shared< ResultEnterRoom >( this, GENERAL_ERROR, nullptr );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultEnterRoom >( this, SUCCESS, room );
|
||||
}
|
||||
|
||||
ResultEnterRoom::ResultEnterRoom( GenericRequest *request, int32_t reply, sptr_chat_room_session room ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_room = room;
|
||||
}
|
||||
|
||||
void ResultEnterRoom::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
if( m_room )
|
||||
{
|
||||
out.write_utf16( m_room->m_name );
|
||||
out.write_utf16( m_room->m_banner );
|
||||
|
||||
out.write_u32( static_cast< uint32_t >( m_room->m_members.size() ) );
|
||||
for( const auto &m : m_room->m_members )
|
||||
{
|
||||
if( auto member = m.lock() )
|
||||
{
|
||||
out.write_utf16( member->m_chatHandle );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write_utf16( L"Unknown" );
|
||||
}
|
||||
}
|
||||
|
||||
out.write_u32( static_cast< uint32_t >( m_room->m_moderators.size() ) );
|
||||
for( const auto &m : m_room->m_moderators )
|
||||
{
|
||||
if( auto member = m.lock() )
|
||||
{
|
||||
out.write_utf16( member->m_chatHandle );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write_utf16( L"" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Source/Network/Event/RequestGetCharacterData_RTA.cpp
Normal file
113
Source/Network/Event/RequestGetCharacterData_RTA.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "Network/Event/RequestGetCharacterData_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetNetCharacterData_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
auto sessionId = stream->read_encrypted_utf16();
|
||||
m_characterId = stream->read_u32();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetNetCharacterData_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultGetNetCharacterData_RTA >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
auto result = Database::Get().LoadCharacterData( user->m_accountId, m_characterId );
|
||||
|
||||
if( !result )
|
||||
{
|
||||
Log::Error( "Failed to load character data for account ID: " + std::to_string( user->m_accountId ) + ", character ID: " + std::to_string( m_characterId ) );
|
||||
return std::make_shared< ResultGetNetCharacterData_RTA >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
user->m_characterId = result->m_characterId;
|
||||
user->m_character = result;
|
||||
|
||||
return SendCharacterData( socket, result );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetNetCharacterData_RTA::SendCharacterData( sptr_socket socket, sptr_realm_character character )
|
||||
{
|
||||
const auto &data = character->m_data;
|
||||
const auto data_size = data.size();
|
||||
size_t position = 0;
|
||||
|
||||
sptr_generic_response finalChunk = nullptr;
|
||||
|
||||
while( position < data_size )
|
||||
{
|
||||
const size_t chunk_size = std::min<size_t>( 1024, data_size - position );
|
||||
|
||||
std::vector<uint8_t> chunk_data( data.begin() + position, data.begin() + position + chunk_size );
|
||||
|
||||
const int32_t isFinalChunk = ( position + chunk_size >= data_size );
|
||||
|
||||
if( isFinalChunk && chunk_data.size() < 1024 )
|
||||
{
|
||||
chunk_data.resize( 1024, 0 );
|
||||
}
|
||||
|
||||
auto response = std::make_shared<ResultGetNetCharacterData_RTA>( this, SUCCESS, std::move( chunk_data ), isFinalChunk );
|
||||
|
||||
if( isFinalChunk )
|
||||
{
|
||||
finalChunk = response;
|
||||
}
|
||||
else
|
||||
{
|
||||
socket->send( response );
|
||||
}
|
||||
|
||||
position += chunk_size;
|
||||
}
|
||||
|
||||
return finalChunk;
|
||||
}
|
||||
|
||||
ResultGetNetCharacterData_RTA::ResultGetNetCharacterData_RTA( GenericRequest *request, int32_t reply, std::optional<std::vector<uint8_t>> data, std::optional<int32_t> endOfData ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
|
||||
if( !data.has_value() )
|
||||
{
|
||||
m_chunk.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_chunk = data.value();
|
||||
}
|
||||
|
||||
if( !endOfData.has_value() )
|
||||
{
|
||||
m_endOfData = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_endOfData = endOfData.value();
|
||||
}
|
||||
}
|
||||
|
||||
void ResultGetNetCharacterData_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
if( !m_reply )
|
||||
{
|
||||
out.write_u32( static_cast< uint32_t >( m_chunk.size() ) );
|
||||
out.write_bytes( m_chunk );
|
||||
out.write_u32( m_endOfData );
|
||||
}
|
||||
}
|
||||
34
Source/Network/Event/RequestGetEncryptionKey.cpp
Normal file
34
Source/Network/Event/RequestGetEncryptionKey.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "Network/Event/RequestGetEncryptionKey.hpp"
|
||||
|
||||
void RequestGetEncryptionKey::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetEncryptionKey::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto publicKey = stream->read_utf8();
|
||||
auto unknown = stream->read_u32();
|
||||
|
||||
return std::make_shared< ResultGetEncryptionKey >( this );
|
||||
}
|
||||
|
||||
ResultGetEncryptionKey::ResultGetEncryptionKey( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
m_symKey = RealmCrypt::getSymmetricKey();
|
||||
}
|
||||
|
||||
void ResultGetEncryptionKey::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
const auto encrypted = RealmCrypt::encryptSymmetric( m_symKey );
|
||||
|
||||
out.write_u32( static_cast<uint32_t>( encrypted.size() ) + 4 );
|
||||
out.write_u32( static_cast<uint32_t>( m_symKey.size() ) );
|
||||
out.write_bytes( encrypted );
|
||||
}
|
||||
38
Source/Network/Event/RequestGetFriendList.cpp
Normal file
38
Source/Network/Event/RequestGetFriendList.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "RequestGetFriendList.h"
|
||||
|
||||
void RequestGetFriendList::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetFriendList::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto publicKey = stream->read_utf8();
|
||||
auto unknown = stream->read_u32();
|
||||
|
||||
return std::make_shared< ResultGetFriendList >( this );
|
||||
}
|
||||
|
||||
ResultGetFriendList::ResultGetFriendList( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
}
|
||||
|
||||
void ResultGetFriendList::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
// Friends
|
||||
out.write_u32(1);
|
||||
out.write_utf16(L"String_1");
|
||||
|
||||
out.write_u32(1);
|
||||
out.write_utf16(L"String_2");
|
||||
|
||||
// Ignore
|
||||
out.write_u32(1);
|
||||
out.write_utf16(L"String_3");
|
||||
}
|
||||
72
Source/Network/Event/RequestGetGame.cpp
Normal file
72
Source/Network/Event/RequestGetGame.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "Network/Event/RequestGetGame.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetGame::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_gameName = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetGame::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultGetGame >( this, TIMEOUT );
|
||||
}
|
||||
|
||||
auto session = GameSessionManager::Get().FindGame( m_gameName, user->m_gameType );
|
||||
|
||||
if( session == nullptr )
|
||||
{
|
||||
Log::Error( "Game session not found! [{}]", m_gameName );
|
||||
return std::make_shared< ResultGetGame >( this, NOT_FOUND );
|
||||
}
|
||||
|
||||
if( session->m_currentPlayers >= session->m_maximumPlayers )
|
||||
{
|
||||
Log::Error( "Game session is full! [{}]", m_gameName );
|
||||
return std::make_shared< ResultGetGame >( this, TIMEOUT );
|
||||
}
|
||||
|
||||
auto host_user = session->GetOwner();
|
||||
|
||||
if( host_user == nullptr )
|
||||
{
|
||||
Log::Error( "Game session owner not found! [{}]", m_gameName );
|
||||
return std::make_shared< ResultGetGame >( this, TIMEOUT );
|
||||
}
|
||||
|
||||
user->m_isHost = false;
|
||||
user->m_gameId = session->m_gameId;
|
||||
|
||||
return std::make_shared< ResultGetGame >( this, SUCCESS, session->m_gameId );
|
||||
}
|
||||
|
||||
ResultGetGame::ResultGetGame( GenericRequest *request, int32_t reply, int32_t gameId ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_gameId = gameId;
|
||||
}
|
||||
|
||||
void ResultGetGame::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
// TODO: These may come in from the UpdateGameData event
|
||||
out.write_utf16( L"Kelethin" );
|
||||
out.write_utf16( L"OwnerName" );
|
||||
|
||||
out.write_u32( m_gameId );
|
||||
}
|
||||
78
Source/Network/Event/RequestGetGame_RTA.cpp
Normal file
78
Source/Network/Event/RequestGetGame_RTA.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "Network/Event/RequestGetGame_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetGame_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_gameName = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetGame_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultGetGame_RTA >( this, TIMEOUT );
|
||||
}
|
||||
|
||||
auto session = GameSessionManager::Get().FindGame( m_gameName, user->m_gameType );
|
||||
|
||||
if( session == nullptr )
|
||||
{
|
||||
Log::Error( "Game session not found! [{}]", m_gameName );
|
||||
return std::make_shared< ResultGetGame_RTA >( this, NOT_FOUND );
|
||||
}
|
||||
|
||||
if( session->m_currentPlayers >= session->m_maximumPlayers )
|
||||
{
|
||||
Log::Error( "Game session is full! [{}]", m_gameName );
|
||||
return std::make_shared< ResultGetGame_RTA >( this, TIMEOUT );
|
||||
}
|
||||
|
||||
auto host_user = session->GetOwner();
|
||||
|
||||
if( host_user == nullptr )
|
||||
{
|
||||
Log::Error( "Game session owner not found! [{}]", m_gameName );
|
||||
return std::make_shared< ResultGetGame_RTA >( this, TIMEOUT );
|
||||
}
|
||||
|
||||
user->m_isHost = false;
|
||||
user->m_gameId = session->m_gameId;
|
||||
|
||||
return std::make_shared< ResultGetGame_RTA >( this, SUCCESS, session->m_gameId, host_user->m_discoveryAddr, host_user->m_localAddr, host_user->m_discoveryPort );
|
||||
}
|
||||
|
||||
ResultGetGame_RTA::ResultGetGame_RTA( GenericRequest *request, int32_t reply, int32_t gameId, std::string discoveryAddr, std::string localAddr, int32_t discoveryPort ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_gameId = gameId;
|
||||
m_discoveryAddr = Util::UTF8ToWide( discoveryAddr );
|
||||
m_localAddr = Util::UTF8ToWide( localAddr );
|
||||
m_port = discoveryPort;
|
||||
}
|
||||
|
||||
void ResultGetGame_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_utf16( L"Test" );
|
||||
out.write_u32( m_gameId );
|
||||
|
||||
out.write_utf16( m_discoveryAddr );
|
||||
out.write_u32( m_port );
|
||||
|
||||
out.write_utf16( m_localAddr );
|
||||
out.write_u32( m_port );
|
||||
}
|
||||
65
Source/Network/Event/RequestGetNetCharacterList_RTA.cpp
Normal file
65
Source/Network/Event/RequestGetNetCharacterList_RTA.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Network/Event/RequestGetNetCharacterList_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmCharacterMetaKV.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetNetCharacterList_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetNetCharacterList_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( !user )
|
||||
{
|
||||
return std::make_shared< ResultGetNetCharacterList_RTA >( this, FATAL_ERROR, std::nullopt );
|
||||
}
|
||||
|
||||
auto result = Database::Get().LoadCharacterSlots( user->m_accountId );
|
||||
|
||||
return std::make_shared< ResultGetNetCharacterList_RTA >( this, SUCCESS, result );
|
||||
}
|
||||
|
||||
ResultGetNetCharacterList_RTA::ResultGetNetCharacterList_RTA( GenericRequest *request, int32_t reply, std::optional< std::map< uint32_t, CharacterSlotData > > list ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
|
||||
if( list != std::nullopt )
|
||||
{
|
||||
m_characterList = list.value();
|
||||
}
|
||||
}
|
||||
|
||||
void ResultGetNetCharacterList_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
// Character Count
|
||||
out.write_u32( static_cast< int >( m_characterList.size() ) );
|
||||
|
||||
// Character List
|
||||
for( auto &character : m_characterList )
|
||||
{
|
||||
const auto &KV = character.second.GetMetaData();
|
||||
|
||||
// Character ID
|
||||
out.write_u32( character.first );
|
||||
|
||||
// Number of Key-Value pairs
|
||||
out.write_u32( static_cast< uint32_t >( KV.size() ) );
|
||||
|
||||
for( auto &pair : KV )
|
||||
{
|
||||
out.write_utf16( pair.first );
|
||||
out.write_utf16( pair.second );
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Source/Network/Event/RequestGetPublicRooms.cpp
Normal file
55
Source/Network/Event/RequestGetPublicRooms.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "Network/Event/RequestGetPublicRooms.hpp"
|
||||
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
|
||||
void RequestGetPublicRooms::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetPublicRooms::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
const auto publicRooms = ChatRoomManager::Get().GetPublicRoomList();
|
||||
|
||||
return std::make_shared< ResultGetPublicRooms >( this, publicRooms );
|
||||
}
|
||||
|
||||
ResultGetPublicRooms::ResultGetPublicRooms( GenericRequest *request, std::vector< sptr_chat_room_session > rooms ) : GenericResponse( *request )
|
||||
{
|
||||
m_rooms = std::move( rooms );
|
||||
}
|
||||
|
||||
void ResultGetPublicRooms::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
const auto numRoom = static_cast< uint32_t >( m_rooms.size() );
|
||||
|
||||
out.write_u32( numRoom );
|
||||
for( const auto &room : m_rooms )
|
||||
{
|
||||
out.write_utf16( room->m_name );
|
||||
}
|
||||
|
||||
out.write_u32( numRoom );
|
||||
for( const auto &room : m_rooms )
|
||||
{
|
||||
out.write_utf16( L"UNKNOWN" );
|
||||
}
|
||||
|
||||
out.write_u32( numRoom );
|
||||
for( const auto &room : m_rooms )
|
||||
{
|
||||
out.write_u32( 88 );
|
||||
}
|
||||
|
||||
out.write_u32( numRoom );
|
||||
for( const auto &room : m_rooms )
|
||||
{
|
||||
out.write_u32( 99 );
|
||||
}
|
||||
}
|
||||
39
Source/Network/Event/RequestGetRealmStats.cpp
Normal file
39
Source/Network/Event/RequestGetRealmStats.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "Network/Event/RequestGetRealmStats.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
|
||||
void RequestGetRealmStats::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetRealmStats::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
return std::make_shared< ResultGetRealmStats >( this );
|
||||
}
|
||||
|
||||
ResultGetRealmStats::ResultGetRealmStats( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ResultGetRealmStats::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
// Player count on the game page.
|
||||
out.write_u32( UserManager::Get().GetUserCount() ); // Users Logged In Game
|
||||
|
||||
// I'm not sure this appears anywhere in the game.
|
||||
out.write_u32( 0 ); // Users Logged In Realm
|
||||
out.write_u32( 0 ); // Users Running Game
|
||||
out.write_u32( 0 ); // Users Running Realm
|
||||
out.write_u32( 0 ); // Users Playing Game
|
||||
out.write_u32( 0 ); // Users Playing Realm
|
||||
out.write_u32( 0 ); // unmatchedGamesGame
|
||||
out.write_u32( 0 ); // unmatchedGamesRealm
|
||||
}
|
||||
86
Source/Network/Event/RequestGetRoom.cpp
Normal file
86
Source/Network/Event/RequestGetRoom.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "Network/Event/RequestGetRoom.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetRoom::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_roomName = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetRoom::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
const auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( !user )
|
||||
{
|
||||
return std::make_shared< ResultGetRoom >( this, GENERAL_ERROR );
|
||||
}
|
||||
|
||||
sptr_chat_room_session targetRoom = nullptr;
|
||||
|
||||
if( m_roomName.empty() )
|
||||
{
|
||||
targetRoom = ChatRoomManager::Get().FindRoom( user->m_privateRoomId );
|
||||
}
|
||||
else
|
||||
{
|
||||
targetRoom = ChatRoomManager::Get().FindRoom( user->m_publicRoomId );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultGetRoom >( this, SUCCESS, targetRoom );
|
||||
}
|
||||
|
||||
ResultGetRoom::ResultGetRoom( GenericRequest *request, int32_t reply, sptr_chat_room_session room ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_room = room;
|
||||
}
|
||||
|
||||
void ResultGetRoom::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
if( m_room )
|
||||
{
|
||||
out.write_utf16( m_room->m_name );
|
||||
out.write_utf16( m_room->m_banner );
|
||||
|
||||
out.write_u32( static_cast< uint32_t >( m_room->m_members.size() ) );
|
||||
|
||||
for( const auto &m : m_room->m_members )
|
||||
{
|
||||
const auto &member = m.lock();
|
||||
|
||||
if( member )
|
||||
{
|
||||
out.write_utf16( member->m_chatHandle );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write_utf16( L"" );
|
||||
}
|
||||
}
|
||||
|
||||
out.write_u32( static_cast< uint32_t >( m_room->m_moderators.size() ) );
|
||||
for( const auto &m : m_room->m_moderators )
|
||||
{
|
||||
if( auto member = m.lock() )
|
||||
{
|
||||
out.write_utf16( member->m_chatHandle );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write_utf16( L"" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Source/Network/Event/RequestGetRules.cpp
Normal file
62
Source/Network/Event/RequestGetRules.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "Network/Event/RequestGetRules.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Common/Constant.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetRules::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_language = stream->read_sz_utf8();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetRules::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultGetRules >( this, L"" );
|
||||
}
|
||||
|
||||
// TODO: Get rules/eula based on language
|
||||
// and move it info a MOTD file.
|
||||
std::wstring rules;
|
||||
|
||||
if( user->m_gameType == RealmGameType::RETURN_TO_ARMS )
|
||||
{
|
||||
rules = L"Welcome to the Champions Emulated Server!\n\n"
|
||||
L"RETURN TO ARMS network support is currently a\n"
|
||||
L"work in progress and can not guarantee stability.\n\n"
|
||||
L"[IMPORTANT]:\n"
|
||||
L"Please note that ONLINE character saves may be unstable.\n"
|
||||
L"Use them at your own risk.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
rules = L"Welcome to the Champions Emulated Server!\n\n"
|
||||
L"This server is currently in development\n"
|
||||
L"and may not be fully functional.\n\n";
|
||||
}
|
||||
|
||||
return std::make_shared< ResultGetRules >( this, rules );
|
||||
}
|
||||
|
||||
ResultGetRules::ResultGetRules( GenericRequest *request, std::wstring rules ) : GenericResponse( *request )
|
||||
{
|
||||
m_rules = rules;
|
||||
}
|
||||
|
||||
void ResultGetRules::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
out.write_utf16( m_rules );
|
||||
|
||||
|
||||
}
|
||||
47
Source/Network/Event/RequestGetServerAddress.cpp
Normal file
47
Source/Network/Event/RequestGetServerAddress.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "Network/Event/RequestGetServerAddress.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Common/Constant.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetServerAddress::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetServerAddress::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
if( socket->gameType == RealmGameType::RETURN_TO_ARMS )
|
||||
{
|
||||
return std::make_shared< ResultGetServerAddress >( this, Config::service_ip, Config::rta_lobby_port, socket->gameType );
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_shared< ResultGetServerAddress >( this, Config::service_ip, Config::con_lobby_port, socket->gameType );
|
||||
}
|
||||
}
|
||||
|
||||
ResultGetServerAddress::ResultGetServerAddress( GenericRequest *request, std::string ip, int32_t port, RealmGameType gameType ) : GenericResponse( *request )
|
||||
{
|
||||
m_ip = ip;
|
||||
m_port = port;
|
||||
m_gameType = gameType;
|
||||
}
|
||||
|
||||
void ResultGetServerAddress::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
if( m_gameType == RealmGameType::RETURN_TO_ARMS )
|
||||
out.write_utf8( m_ip );
|
||||
else
|
||||
out.write_sz_utf8( m_ip );
|
||||
|
||||
out.write( m_port );
|
||||
}
|
||||
54
Source/Network/Event/RequestGetSocialListInitial.cpp
Normal file
54
Source/Network/Event/RequestGetSocialListInitial.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "Network/Event/RequestGetSocialListInitial.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetSocialListInitial::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetSocialListInitial::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "RequestGetFriendList::ProcessRequest() - User not found" );
|
||||
return std::make_shared< ResultGetSocialListInitial >( this );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultGetSocialListInitial >( this, user );
|
||||
}
|
||||
|
||||
ResultGetSocialListInitial::ResultGetSocialListInitial( GenericRequest *request, sptr_user user ) : GenericResponse( *request )
|
||||
{
|
||||
m_user = user;
|
||||
}
|
||||
|
||||
void ResultGetSocialListInitial::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
// Friends
|
||||
out.write_u32( static_cast< uint32_t >( m_user->m_friendList.size() ) );
|
||||
for( const auto &friendHandle : m_user->m_friendList )
|
||||
{
|
||||
out.write_utf16( friendHandle );
|
||||
}
|
||||
|
||||
// ?
|
||||
out.write_u32(1);
|
||||
out.write_utf16(L"String_2");
|
||||
|
||||
// Ignore
|
||||
out.write_u32( static_cast< uint32_t >( m_user->m_ignoreList.size() ) );
|
||||
for( const auto &ignoreHandle : m_user->m_ignoreList )
|
||||
{
|
||||
out.write_utf16( ignoreHandle );
|
||||
}
|
||||
}
|
||||
50
Source/Network/Event/RequestGetSocialListUpdate.cpp
Normal file
50
Source/Network/Event/RequestGetSocialListUpdate.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "Network/Event/RequestGetSocialListUpdate.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestGetSocialListUpdate::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestGetSocialListUpdate::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "RequestGetFriendList::ProcessRequest() - User not found" );
|
||||
return std::make_shared< ResultGetSocialListUpdate >( this );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultGetSocialListUpdate >( this, user );
|
||||
}
|
||||
|
||||
ResultGetSocialListUpdate::ResultGetSocialListUpdate( GenericRequest *request, sptr_user user ) : GenericResponse( *request )
|
||||
{
|
||||
m_user = user;
|
||||
}
|
||||
|
||||
void ResultGetSocialListUpdate::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
// Friends
|
||||
out.write_u32( static_cast< uint32_t >( m_user->m_friendList.size() ) );
|
||||
for( const auto &friendHandle : m_user->m_friendList )
|
||||
{
|
||||
out.write_utf16( friendHandle );
|
||||
}
|
||||
|
||||
// Ignore
|
||||
out.write_u32( static_cast< uint32_t >( m_user->m_ignoreList.size() ) );
|
||||
for( const auto &ignoreHandle : m_user->m_ignoreList )
|
||||
{
|
||||
out.write_utf16( ignoreHandle );
|
||||
}
|
||||
}
|
||||
45
Source/Network/Event/RequestLeaveRoom.cpp
Normal file
45
Source/Network/Event/RequestLeaveRoom.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "Network/Event/RequestLeaveRoom.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestLeaveRoom::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
auto sessionId = stream->read_encrypted_utf16();
|
||||
m_roomName = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestLeaveRoom::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
const auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( !user )
|
||||
{
|
||||
Log::Error( "User not found for socket!" );
|
||||
return std::make_shared< ResultLeaveRoom >( this, GENERAL_ERROR );
|
||||
}
|
||||
|
||||
if( !ChatRoomManager::Get().LeaveRoom( user, m_roomName ) )
|
||||
{
|
||||
Log::Error( "Could not remove user [{}] from room [{}]", user->m_username, m_roomName );
|
||||
return std::make_shared< ResultLeaveRoom >( this, GENERAL_ERROR );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultLeaveRoom >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultLeaveRoom::ResultLeaveRoom( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultLeaveRoom::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
122
Source/Network/Event/RequestLogin.cpp
Normal file
122
Source/Network/Event/RequestLogin.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "Network/Event/RequestLogin.hpp"
|
||||
|
||||
#include "Database/Database.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Common/Constant.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestLogin::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_username = stream->read_encrypted_utf16();
|
||||
m_password = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestLogin::ProcessLoginCON( sptr_user user )
|
||||
{
|
||||
if( m_username != L"foo" && m_password != L"bar" )
|
||||
{
|
||||
// Network Beta CoN uses login information, but it's invalid because of version 2.0
|
||||
// which used "foo" and "bar" as the login credentials.
|
||||
Log::Debug( "RequestLogin : Champions of Norrath v1.0" );
|
||||
|
||||
// TODO: Either block this, or add support for the network beta.
|
||||
return std::make_shared< ResultLogin >( this, LOGIN_REPLY::ACCOUNT_INVALID, L"" );
|
||||
}
|
||||
|
||||
user->m_isLoggedIn = true;
|
||||
user->m_accountId = -1;
|
||||
user->m_sessionId = UserManager::Get().GenerateSessionId();
|
||||
|
||||
return std::make_shared< ResultLogin >( this, SUCCESS, user->m_sessionId );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestLogin::ProcessLoginRTA( sptr_user user )
|
||||
{
|
||||
// Return to Arms uses login information.
|
||||
Log::Debug( "RequestLogin : Return to Arms" );
|
||||
|
||||
auto &UserManager = UserManager::Get();
|
||||
auto &Database = Database::Get();
|
||||
|
||||
// Verify the account exists
|
||||
auto [ result, accountId, chatHandle ] = Database.VerifyAccount( m_username, m_password );
|
||||
|
||||
if( accountId < 0 )
|
||||
{
|
||||
Log::Error( "RequestLogin::ProcessRequest() - Invalid account ID: " + std::to_string( accountId ) );
|
||||
return std::make_shared< ResultLogin >( this, ACCOUNT_INVALID, L"" );
|
||||
}
|
||||
|
||||
// Check if the user is already logged in
|
||||
for( const auto &existingUser : UserManager.GetUserList() )
|
||||
{
|
||||
if( existingUser->m_username == m_username || existingUser->m_accountId == accountId )
|
||||
{
|
||||
return std::make_shared< ResultLogin >( this, FATAL_ERROR, L"" );
|
||||
}
|
||||
}
|
||||
|
||||
// Login Success
|
||||
user->m_isLoggedIn = true;
|
||||
user->m_username = m_username;
|
||||
user->m_accountId = accountId;
|
||||
user->m_chatHandle = chatHandle;
|
||||
user->m_sessionId = UserManager.GenerateSessionId();
|
||||
|
||||
// Load Friend List
|
||||
user->m_friendList = Database.LoadFriends( accountId );
|
||||
|
||||
// Load Ignore List
|
||||
user->m_ignoreList = Database.LoadIgnores( accountId );
|
||||
|
||||
// Notify friends about the user's online status
|
||||
UserManager.NotifyFriendsOnlineStatus( user, true );
|
||||
|
||||
return std::make_shared< ResultLogin >( this, SUCCESS, user->m_sessionId );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestLogin::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "RequestLogin::ProcessRequest() - User not found" );
|
||||
return std::make_shared< ResultLogin >( this, ACCOUNT_INVALID, L"" );
|
||||
}
|
||||
|
||||
if( m_username.empty() || m_password.empty() )
|
||||
{
|
||||
Log::Error( "RequestLogin::ProcessRequest() - Username or password is empty" );
|
||||
return std::make_shared< ResultLogin >( this, ACCOUNT_INVALID, L"" );
|
||||
}
|
||||
|
||||
if( user->m_gameType == RealmGameType::CHAMPIONS_OF_NORRATH )
|
||||
{
|
||||
return ProcessLoginCON( user );
|
||||
}
|
||||
else
|
||||
{
|
||||
return ProcessLoginRTA( user );
|
||||
}
|
||||
}
|
||||
|
||||
ResultLogin::ResultLogin( GenericRequest *request, int32_t reply, std::wstring sessionId ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
m_sessionId = sessionId;
|
||||
}
|
||||
|
||||
void ResultLogin::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
|
||||
out.write_encrypted_utf16( m_sessionId );
|
||||
out.write_encrypted_utf16( L"UNKNOWN DUMMY STRING" );
|
||||
}
|
||||
33
Source/Network/Event/RequestLogout.cpp
Normal file
33
Source/Network/Event/RequestLogout.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "Network/Event/RequestLogout.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestLogout::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestLogout::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
UserManager::Get().RemoveUser( socket );
|
||||
|
||||
Log::Debug( "[{}] Logout", m_sessionId );
|
||||
|
||||
return std::make_shared< ResultLogout >( this, 0 );
|
||||
}
|
||||
|
||||
ResultLogout::ResultLogout( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultLogout::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
97
Source/Network/Event/RequestMatchGame.cpp
Normal file
97
Source/Network/Event/RequestMatchGame.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "Network/Event/RequestMatchGame.hpp"
|
||||
|
||||
#include <format>
|
||||
|
||||
#include "Common/Constant.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
|
||||
void RequestMatchGame::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
|
||||
auto unknown_a = stream->read_u16();
|
||||
auto unknown_b = stream->read_u32();
|
||||
auto unknown_c = stream->read_u32();
|
||||
auto unknown_d = stream->read_u32();
|
||||
|
||||
auto unknown_e = stream->read_u32();
|
||||
|
||||
// Match Game Node Count
|
||||
for( uint32_t i = 0; i < unknown_e; i++ )
|
||||
{
|
||||
auto node_a = stream->read_u16();
|
||||
auto node_b = stream->read_u32();
|
||||
|
||||
auto node_c = stream->read_utf16();
|
||||
|
||||
auto node_d = stream->read_u32();
|
||||
auto node_e = stream->read_u32();
|
||||
auto node_f = stream->read_u32();
|
||||
auto node_g = stream->read_u16();
|
||||
}
|
||||
|
||||
auto unknown_f = stream->read_u8();
|
||||
auto unknown_g = stream->read_u32();
|
||||
auto unknown_h = stream->read_u32();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestMatchGame::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
return std::make_shared< ResultMatchGame >( this, socket->remote_ip );
|
||||
}
|
||||
|
||||
ResultMatchGame::ResultMatchGame( GenericRequest *request, std::string userIp ) : GenericResponse( *request )
|
||||
{
|
||||
m_userIp = userIp;
|
||||
}
|
||||
|
||||
void ResultMatchGame::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
const auto publicGameList = GameSessionManager::Get().GetAvailableGameSessionList( RealmGameType::CHAMPIONS_OF_NORRATH );
|
||||
const auto publicGameCount = static_cast< uint32_t >( publicGameList.size() );
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
{
|
||||
if( m_userIp == game->m_hostExternalAddr )
|
||||
out.write_utf16( std::format( L"{}:{}", Util::UTF8ToWide( game->m_hostLocalAddr ), game->m_hostNatPort ) );
|
||||
else
|
||||
out.write_utf16( std::format( L"{}:{}", Util::UTF8ToWide( game->m_hostExternalAddr ), game->m_hostNatPort ) );
|
||||
}
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_utf16( game->m_gameName );
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_utf16( game->m_ownerName );
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_u32( game->m_gameId );
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_utf8( game->m_gameData );
|
||||
}
|
||||
}
|
||||
93
Source/Network/Event/RequestMatchGame_RTA.cpp
Normal file
93
Source/Network/Event/RequestMatchGame_RTA.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "Network/Event/RequestMatchGame_RTA.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestMatchGame_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestMatchGame_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
UserManager::Get().Disconnect( socket, "User not found!" );
|
||||
return std::make_shared< ResultMatchGame_RTA >( this, "" );
|
||||
}
|
||||
|
||||
if( !user->m_isLoggedIn )
|
||||
{
|
||||
UserManager::Get().Disconnect( user, "User is not logged in!" );
|
||||
return std::make_shared< ResultMatchGame_RTA >( this, "" );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultMatchGame_RTA >( this, socket->remote_ip );
|
||||
}
|
||||
|
||||
ResultMatchGame_RTA::ResultMatchGame_RTA( GenericRequest *request, std::string userIp ) : GenericResponse( *request )
|
||||
{
|
||||
m_userIp = userIp;
|
||||
}
|
||||
|
||||
void ResultMatchGame_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
|
||||
const auto publicGameList = GameSessionManager::Get().GetAvailableGameSessionList( RealmGameType::RETURN_TO_ARMS );
|
||||
const auto publicGameCount = static_cast< uint32_t >( publicGameList.size() );
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_utf16( Util::UTF8ToWide( game->m_gameData ) );
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_utf16( game->m_playerCount );
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_u32( game->m_gameId );
|
||||
}
|
||||
|
||||
// Something about filtering.
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
out.write_u32( 0 ); // Size
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_utf16( Util::UTF8ToWide( game->m_hostLocalAddr ) );
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_u32( game->m_hostLocalPort );
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_utf16( Util::UTF8ToWide( game->m_hostExternalAddr ) );
|
||||
}
|
||||
|
||||
out.write_u32( publicGameCount );
|
||||
{
|
||||
for( const auto &game : publicGameList )
|
||||
out.write_u32( game->m_hostNatPort );
|
||||
}
|
||||
}
|
||||
56
Source/Network/Event/RequestRemoveFriend.cpp
Normal file
56
Source/Network/Event/RequestRemoveFriend.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "Network/Event/RequestRemoveFriend.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestRemoveFriend::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_chatHandle = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestRemoveFriend::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultRemoveFriend >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( !user->IsFriend( m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultRemoveFriend >( this, FRIEND_INVALID );
|
||||
}
|
||||
|
||||
if( !Database::Get().RemoveFriend( user->m_accountId, m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultRemoveFriend >( this, DATABASE_ERROR );
|
||||
}
|
||||
|
||||
const auto iter = std::find( user->m_friendList.begin(), user->m_friendList.end(), m_chatHandle );
|
||||
if( iter == user->m_friendList.end() )
|
||||
{
|
||||
return std::make_shared< ResultRemoveFriend >( this, FRIEND_INVALID );
|
||||
}
|
||||
|
||||
user->m_friendList.erase( iter );
|
||||
|
||||
return std::make_shared< ResultRemoveFriend >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultRemoveFriend::ResultRemoveFriend( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultRemoveFriend::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
56
Source/Network/Event/RequestRemoveIgnore.cpp
Normal file
56
Source/Network/Event/RequestRemoveIgnore.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "Network/Event/RequestRemoveIgnore.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestRemoveIgnore::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_chatHandle = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestRemoveIgnore::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultRemoveIgnore >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( !user->IsIgnored( m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultRemoveIgnore >( this, IGNORE_INVALID );
|
||||
}
|
||||
|
||||
if( !Database::Get().RemoveIgnore( user->m_accountId, m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultRemoveIgnore >( this, DATABASE_ERROR );
|
||||
}
|
||||
|
||||
const auto iter = std::find( user->m_ignoreList.begin(), user->m_ignoreList.end(), m_chatHandle );
|
||||
if( iter == user->m_ignoreList.end() )
|
||||
{
|
||||
return std::make_shared< ResultRemoveIgnore >( this, IGNORE_INVALID );
|
||||
}
|
||||
|
||||
user->m_ignoreList.erase( iter );
|
||||
|
||||
return std::make_shared< ResultRemoveIgnore >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultRemoveIgnore::ResultRemoveIgnore( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultRemoveIgnore::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
75
Source/Network/Event/RequestSaveCharacter_RTA.cpp
Normal file
75
Source/Network/Event/RequestSaveCharacter_RTA.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "Network/Event/RequestSaveCharacter_RTA.hpp"
|
||||
|
||||
#include "Database/Database.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/CharacterSaveManager.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Game/RealmCharacter.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
#include "Common/RLEZ.hpp"
|
||||
|
||||
void RequestSaveCharacter_RTA::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_memberSessionId = stream->read_encrypted_utf16();
|
||||
m_targetCharacterId = stream->read_u32();
|
||||
|
||||
auto a = stream->read_u32();
|
||||
auto b = stream->read_u32();
|
||||
|
||||
m_metaData.Deserialize( stream );
|
||||
auto characterDataSize = stream->read_u32();
|
||||
m_characterData = stream->read_bytes( characterDataSize );
|
||||
}
|
||||
|
||||
sptr_generic_response RequestSaveCharacter_RTA::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto &userManager = UserManager::Get();
|
||||
|
||||
auto user = userManager.FindUserBySocket( socket );
|
||||
if( user == nullptr || user->m_accountId == -1 )
|
||||
{
|
||||
return std::make_shared< ResultSaveCharacter_RTA >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
sptr_user targetUser = user;
|
||||
|
||||
if( m_sessionId != m_memberSessionId )
|
||||
{
|
||||
targetUser = userManager.FindUserBySessionId( m_memberSessionId );
|
||||
}
|
||||
|
||||
if( targetUser == nullptr || targetUser->m_accountId == -1 )
|
||||
{
|
||||
Log::Error( "Target user not found or invalid account ID for session: {}", m_memberSessionId );
|
||||
return std::make_shared< ResultSaveCharacter_RTA >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
auto characterId = targetUser->m_characterId;
|
||||
|
||||
auto &saveManager = CharacterSaveManager::Get();
|
||||
if( saveManager.BeginSaveTask( user, targetUser, characterId, m_metaData, CharacterSaveType::SAVE_CHARACTER ) )
|
||||
{
|
||||
saveManager.AppendSaveData( user->m_sessionId, m_characterData, false );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultSaveCharacter_RTA >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultSaveCharacter_RTA::ResultSaveCharacter_RTA( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultSaveCharacter_RTA::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
54
Source/Network/Event/RequestSendInstantMessage.cpp
Normal file
54
Source/Network/Event/RequestSendInstantMessage.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "Network/Event/RequestSendInstantMessage.hpp"
|
||||
|
||||
#include "Network\Event\NotifyInstantMessage.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestSendInstantMessage::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
auto sessionId = stream->read_encrypted_utf16();
|
||||
m_chatHandle = stream->read_utf16();
|
||||
m_message = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestSendInstantMessage::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
const auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( !user )
|
||||
{
|
||||
Log::Error( "User not found for socket!" );
|
||||
return std::make_shared< ResultSendInstantMessage >( this, GENERAL_ERROR );
|
||||
}
|
||||
|
||||
auto targetUser = UserManager::Get().FindUserByChatHandle( m_chatHandle );
|
||||
if( !targetUser )
|
||||
{
|
||||
return std::make_shared< ResultSendInstantMessage >( this, GENERAL_ERROR );
|
||||
}
|
||||
|
||||
if( targetUser->IsIgnored( user->m_chatHandle ) )
|
||||
{
|
||||
return std::make_shared< ResultSendInstantMessage >( this, USER_IGNORED );
|
||||
}
|
||||
|
||||
targetUser->sock->send( NotifyInstantMessage( user->m_chatHandle, m_message ) );
|
||||
|
||||
return std::make_shared< ResultSendInstantMessage >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultSendInstantMessage::ResultSendInstantMessage( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultSendInstantMessage::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
72
Source/Network/Event/RequestSendRoomMessage.cpp
Normal file
72
Source/Network/Event/RequestSendRoomMessage.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "Network/Event/RequestSendRoomMessage.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
|
||||
#include "Network\Event\NotifyRoomMessage.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestSendRoomMessage::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
auto sessionId = stream->read_encrypted_utf16();
|
||||
m_roomName = stream->read_utf16();
|
||||
m_message = stream->read_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestSendRoomMessage::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
const auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( !user )
|
||||
{
|
||||
Log::Error( "User not found for socket!" );
|
||||
return std::make_shared< ResultSendRoomMessage >( this, GENERAL_ERROR );
|
||||
}
|
||||
|
||||
const auto room = ChatRoomManager::Get().FindRoom( m_roomName );
|
||||
|
||||
if( !room )
|
||||
{
|
||||
Log::Error( "Chat room not found: {}", m_roomName );
|
||||
return std::make_shared< ResultSendRoomMessage >( this, GENERAL_ERROR );
|
||||
}
|
||||
|
||||
if( !room->IsMember( user ) )
|
||||
{
|
||||
Log::Error( "User [{}] is not a member of chat room [{}]", user->m_chatHandle, m_roomName );
|
||||
return std::make_shared< ResultSendRoomMessage >( this, GENERAL_ERROR );
|
||||
}
|
||||
|
||||
NotifyRoomMessage msg( m_roomName, user->m_chatHandle, m_message );
|
||||
|
||||
for( const auto &member : room->m_members )
|
||||
{
|
||||
auto memberUser = member.lock();
|
||||
if( !memberUser )
|
||||
continue;
|
||||
|
||||
if( memberUser->IsIgnored( user->m_chatHandle ) )
|
||||
{
|
||||
continue; // Skip sending to ignored users
|
||||
}
|
||||
|
||||
memberUser->sock->send( msg );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultSendRoomMessage >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultSendRoomMessage::ResultSendRoomMessage( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultSendRoomMessage::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
57
Source/Network/Event/RequestStartGame.cpp
Normal file
57
Source/Network/Event/RequestStartGame.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "Network/Event/RequestStartGame.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/ChatRoomManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestStartGame::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
auto sessionId = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestStartGame::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultStartGame >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
auto session = GameSessionManager::Get().FindGame( user->m_gameId, user->m_gameType );
|
||||
if( session == nullptr )
|
||||
{
|
||||
Log::Error( "Game session not found! [%d]", user->m_gameId );
|
||||
return std::make_shared< ResultStartGame >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( !GameSessionManager::Get().RequestStart( user ) )
|
||||
{
|
||||
Log::Error( "Failed to start game session [{}]", user->m_gameId );
|
||||
return std::make_shared< ResultStartGame >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( !ChatRoomManager::Get().CloseGameChatSession( session->m_gameName ) )
|
||||
{
|
||||
Log::Error( "Failed to close chat room for game session [{}]", session->m_gameName );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultStartGame >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultStartGame::ResultStartGame( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultStartGame::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
38
Source/Network/Event/RequestTouchSession.cpp
Normal file
38
Source/Network/Event/RequestTouchSession.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "Network/Event/RequestTouchSession.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestTouchSession::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestTouchSession::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultTouchSession >( this );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultTouchSession >( this );
|
||||
}
|
||||
|
||||
ResultTouchSession::ResultTouchSession( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ResultTouchSession::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
}
|
||||
91
Source/Network/Event/RequestUpdateGameData.cpp
Normal file
91
Source/Network/Event/RequestUpdateGameData.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
#include "Network/Event/RequestUpdateGameData.hpp"
|
||||
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestUpdateGameData::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_gameData = stream->read_utf8();
|
||||
}
|
||||
|
||||
bool RequestUpdateGameData::ParseGameData( sptr_game_session session )
|
||||
{
|
||||
if( session == nullptr || m_gameData.size() < 256 )
|
||||
{
|
||||
Log::Error( "Invalid game session or game data size! [{}]", m_sessionId );
|
||||
return false;
|
||||
}
|
||||
|
||||
session->m_gameData = m_gameData;
|
||||
|
||||
int8_t currentPlayers = 0;
|
||||
int8_t maxPlayers = 0;
|
||||
char description[ 200 ] = { 0 };
|
||||
|
||||
int result = sscanf( m_gameData.c_str(), " %hhd / %hhd :%199[^\r\n]", ¤tPlayers, &maxPlayers, description );
|
||||
|
||||
if( result >= 2 )
|
||||
{
|
||||
session->m_currentPlayers = currentPlayers;
|
||||
session->m_maximumPlayers = maxPlayers;
|
||||
session->m_description = ( result == 3 ) ? description : "";
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::Debug( "Failed to parse game info from: {}", m_gameData );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sptr_generic_response RequestUpdateGameData::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
|
||||
if( user == nullptr )
|
||||
{
|
||||
Log::Error( "User not found! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultUpdateGameData >( this );
|
||||
}
|
||||
|
||||
auto gameSession = GameSessionManager::Get().FindGame( user->m_gameId, user->m_gameType );
|
||||
|
||||
if( !ParseGameData( gameSession ) )
|
||||
{
|
||||
Log::Error( "Failed to parse game data! [{}]", m_sessionId );
|
||||
return std::make_shared< ResultUpdateGameData >( this );
|
||||
}
|
||||
|
||||
const auto localAddr = std::string( m_gameData.c_str() + 220, 24 );
|
||||
user->m_localAddr = localAddr;
|
||||
|
||||
return std::make_shared< ResultUpdateGameData >( this );
|
||||
}
|
||||
|
||||
ResultUpdateGameData::ResultUpdateGameData( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ResultUpdateGameData::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( 0 );
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
59
Source/Network/Event/RequestUserJoinSuccess.cpp
Normal file
59
Source/Network/Event/RequestUserJoinSuccess.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "Network/Event/RequestUserJoinSuccess.hpp"
|
||||
|
||||
#include "Game/GameSessionManager.hpp"
|
||||
#include "Game/RealmUserManager.hpp"
|
||||
#include "Game/RealmUser.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
void RequestUserJoinSuccess::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_ownerSessionId = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response RequestUserJoinSuccess::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
if( user == nullptr )
|
||||
{
|
||||
return std::make_shared< ResultUserJoinSuccess >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
auto gameSession = GameSessionManager::Get().FindGame( user->m_gameId, user->m_gameType );
|
||||
if( gameSession == nullptr )
|
||||
{
|
||||
Log::Error( "Game session not found for user: {}", user->m_username );
|
||||
return std::make_shared< ResultUserJoinSuccess >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( !gameSession->IsJoinable() )
|
||||
{
|
||||
Log::Error( "Game session is not open for user: {}", user->m_username );
|
||||
return std::make_shared< ResultUserJoinSuccess >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
if( !gameSession->AddMember( user ) )
|
||||
{
|
||||
Log::Error( "Failed to add user {} to game session {}", user->m_username, gameSession->m_gameId );
|
||||
return std::make_shared< ResultUserJoinSuccess >( this, FATAL_ERROR );
|
||||
}
|
||||
|
||||
return std::make_shared< ResultUserJoinSuccess >( this, SUCCESS );
|
||||
}
|
||||
|
||||
ResultUserJoinSuccess::ResultUserJoinSuccess( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
void ResultUserJoinSuccess::Serialize( ByteBuffer &out ) const
|
||||
{
|
||||
out.write_u16( m_packetId );
|
||||
out.write_u32( m_trackId );
|
||||
out.write_u32( m_reply );
|
||||
}
|
||||
72
Source/Network/Event/Request_5F.cpp
Normal file
72
Source/Network/Event/Request_5F.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "Request_5F.h"
|
||||
|
||||
#include "../../Game/GameSessionManager.h"
|
||||
#include "../../Game/RealmUserManager.h"
|
||||
#include "../../Game/RealmUser.h"
|
||||
#include "../../Database/Database.h"
|
||||
#include "../../logging.h"
|
||||
|
||||
void Request_5F::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
|
||||
m_sessionId = stream->read_encrypted_utf16();
|
||||
m_memberSessionId = stream->read_encrypted_utf16();
|
||||
}
|
||||
|
||||
sptr_generic_response Request_5F::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
Log::Packet( stream->get_buffer(), stream->get_length(), false );
|
||||
|
||||
//auto user = UserManager::Get().FindUserBySocket( socket );
|
||||
//if( user == nullptr )
|
||||
//{
|
||||
// return std::make_shared< Result_5F >( this, FATAL_ERROR );
|
||||
//}
|
||||
//
|
||||
//auto targetUser = UserManager::Get().FindUserBySessionId( m_memberSessionId );
|
||||
//if( targetUser == nullptr || targetUser->m_accountId == -1 )
|
||||
//{
|
||||
// Log::Error( "Target user not found or invalid account ID for session: %S", m_memberSessionId.c_str() );
|
||||
// return std::make_shared< Result_5F >( this, FATAL_ERROR );
|
||||
//}
|
||||
//
|
||||
//auto gameSession = GameSessionManager::Get().FindGame( user->m_gameId, user->m_gameType );
|
||||
//if( gameSession == nullptr )
|
||||
//{
|
||||
// Log::Error( "Game session not found for user: %S", user->m_sessionId.c_str() );
|
||||
// return std::make_shared< Result_5F >( this, FATAL_ERROR );
|
||||
//}
|
||||
//
|
||||
//if( !gameSession->IsJoinable() )
|
||||
//{
|
||||
// Log::Error( "Game session is not open for user: %S", user->m_sessionId.c_str() );
|
||||
// return std::make_shared< Result_5F >( this, FATAL_ERROR );
|
||||
//}
|
||||
//
|
||||
//if( !gameSession->AddMember( targetUser ) )
|
||||
//{
|
||||
// Log::Error( "Failed to add user %S to game session %d", targetUser->m_sessionId.c_str(), gameSession->m_gameIndex );
|
||||
// return std::make_shared< Result_5F >( this, FATAL_ERROR );
|
||||
//}
|
||||
|
||||
// TODO: Here is more like finalize/confirm joined
|
||||
|
||||
return std::make_shared< Result_5F >( this, SUCCESS );
|
||||
}
|
||||
|
||||
Result_5F::Result_5F( GenericRequest *request, int32_t reply ) : GenericResponse( *request )
|
||||
{
|
||||
m_reply = reply;
|
||||
}
|
||||
|
||||
ByteBuffer& Result_5F::Serialize()
|
||||
{
|
||||
m_stream.write_u16( m_packetId );
|
||||
m_stream.write_u32( m_trackId );
|
||||
m_stream.write_u32( m_reply );
|
||||
|
||||
return m_stream;
|
||||
}
|
||||
40
Source/Network/Event/Request_61.cpp
Normal file
40
Source/Network/Event/Request_61.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "Request_61.h"
|
||||
|
||||
void Request_61::Deserialize( sptr_byte_stream stream )
|
||||
{
|
||||
DeserializeHeader( stream );
|
||||
}
|
||||
|
||||
sptr_generic_response Request_61::ProcessRequest( sptr_socket socket, sptr_byte_stream stream )
|
||||
{
|
||||
Deserialize( stream );
|
||||
|
||||
auto publicKey = stream->read_utf8();
|
||||
auto unknown = stream->read_u32();
|
||||
|
||||
return std::make_shared< Result_61 >( this );
|
||||
}
|
||||
|
||||
Result_61::Result_61( GenericRequest *request ) : GenericResponse( *request )
|
||||
{
|
||||
}
|
||||
|
||||
ByteBuffer&Result_61::Serialize()
|
||||
{
|
||||
m_stream.write_u16( m_packetId );
|
||||
m_stream.write_u32( m_trackId );
|
||||
m_stream.write_u32( 0 );
|
||||
|
||||
// Friends
|
||||
m_stream.write_u32(1);
|
||||
m_stream.write_utf16(L"String_1");
|
||||
|
||||
m_stream.write_u32(1);
|
||||
m_stream.write_utf16(L"String_2");
|
||||
|
||||
// Ignore
|
||||
m_stream.write_u32(1);
|
||||
m_stream.write_utf16(L"String_3");
|
||||
|
||||
return m_stream;
|
||||
}
|
||||
1
Source/Network/GenericNetRequest.cpp
Normal file
1
Source/Network/GenericNetRequest.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "Network/GenericNetRequest.hpp"
|
||||
80
Source/Network/RealmSocket.cpp
Normal file
80
Source/Network/RealmSocket.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "Network/RealmSocket.hpp"
|
||||
#include "Common/Utility.hpp"
|
||||
|
||||
RealmSocket::RealmSocket()
|
||||
{
|
||||
fd = INVALID_SOCKET;
|
||||
|
||||
std::memset( &local_addr, 0, sizeof( local_addr ) );
|
||||
std::memset( &remote_addr, 0, sizeof( remote_addr ) );
|
||||
|
||||
remote_ip = "";
|
||||
remote_port = 0;
|
||||
|
||||
flag.disconnected_wait = 0;
|
||||
flag.disconnected_forced = 0;
|
||||
flag.is_listener = 0;
|
||||
flag.is_gateway = 0;
|
||||
flag.want_more_read_data = 0;
|
||||
flag.want_more_write_data = 0;
|
||||
|
||||
last_write_position = 0;
|
||||
|
||||
last_recv_time = std::chrono::steady_clock::now();
|
||||
last_send_time = std::chrono::steady_clock::now();
|
||||
|
||||
latency = 0;
|
||||
}
|
||||
|
||||
RealmSocket::~RealmSocket()
|
||||
{
|
||||
if( INVALID_SOCKET != fd )
|
||||
{
|
||||
closesocket( fd );
|
||||
}
|
||||
|
||||
fd = INVALID_SOCKET;
|
||||
|
||||
std::memset( &local_addr, 0, sizeof( local_addr ) );
|
||||
std::memset( &remote_addr, 0, sizeof( remote_addr ) );
|
||||
|
||||
remote_ip = "";
|
||||
remote_port = 0;
|
||||
|
||||
flag.disconnected_wait = 0;
|
||||
flag.disconnected_forced = 0;
|
||||
flag.is_listener = 0;
|
||||
flag.is_gateway = 0;
|
||||
flag.want_more_read_data = 0;
|
||||
flag.want_more_write_data = 0;
|
||||
|
||||
last_write_position = 0;
|
||||
|
||||
latency = 0;
|
||||
|
||||
m_pendingWriteBuffer.reserve( WRITE_BUFFER_SIZE );
|
||||
}
|
||||
|
||||
void RealmSocket::send( const sptr_generic_response response )
|
||||
{
|
||||
ByteBuffer stream;
|
||||
response->Serialize( stream );
|
||||
const auto netSize = Util::ByteSwap( static_cast< uint32_t >( stream.get_position() ) + 4 );
|
||||
|
||||
std::lock_guard< std::mutex > lock( write_mutex );
|
||||
|
||||
m_pendingWriteBuffer.insert( m_pendingWriteBuffer.end(), ( uint8_t * )&netSize, ( uint8_t * )&netSize + 4 );
|
||||
m_pendingWriteBuffer.insert( m_pendingWriteBuffer.end(), stream.m_buffer.begin(), stream.m_buffer.end() );
|
||||
}
|
||||
|
||||
void RealmSocket::send( const GenericMessage &message )
|
||||
{
|
||||
ByteBuffer stream;
|
||||
message.Serialize( stream );
|
||||
const auto netSize = Util::ByteSwap( static_cast< uint32_t >( stream.get_position() ) + 4 );
|
||||
|
||||
std::lock_guard< std::mutex > lock( write_mutex );
|
||||
|
||||
m_pendingWriteBuffer.insert( m_pendingWriteBuffer.end(), ( uint8_t * )&netSize, ( uint8_t * )&netSize + 4 );
|
||||
m_pendingWriteBuffer.insert( m_pendingWriteBuffer.end(), stream.m_buffer.begin(), stream.m_buffer.end() );
|
||||
}
|
||||
54
Source/configuration.cpp
Normal file
54
Source/configuration.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "configuration.hpp"
|
||||
|
||||
bool Config::Load( std::string filename )
|
||||
{
|
||||
service_ip = "0.0.0.0";
|
||||
con_lobby_port = 40900;
|
||||
rta_lobby_port = 40910;
|
||||
discovery_port = 10101;
|
||||
|
||||
// Read configuration from ini file
|
||||
std::ifstream file( filename );
|
||||
if( !file.is_open() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
|
||||
while( std::getline( file, line ) )
|
||||
{
|
||||
if( line.empty() || line[0] == '#' || line[0] == ';' )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pos = line.find( '=' );
|
||||
if( pos == std::string::npos )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key = line.substr( 0, pos );
|
||||
std::string value = line.substr( pos + 1 );
|
||||
|
||||
if( key == "service_ip" )
|
||||
{
|
||||
service_ip = value;
|
||||
}
|
||||
else if( key == "con_lobby_port" )
|
||||
{
|
||||
con_lobby_port = std::stoi( value );
|
||||
}
|
||||
else if (key == "rta_lobby_port")
|
||||
{
|
||||
rta_lobby_port = std::stoi(value);
|
||||
}
|
||||
else if( key == "discovery_port" )
|
||||
{
|
||||
discovery_port = std::stoi( value );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
191
Source/logging.cpp
Normal file
191
Source/logging.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "logging.hpp"
|
||||
|
||||
static const char *LOG_PATH[] = {
|
||||
"./generic",
|
||||
"./debug",
|
||||
"./error",
|
||||
"./warning",
|
||||
};
|
||||
|
||||
Log::Log()
|
||||
{
|
||||
current_open_hour = 0;
|
||||
}
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
}
|
||||
|
||||
const char *Log::GetTimeStamp()
|
||||
{
|
||||
static char timestamp[ 64 ] = "";
|
||||
|
||||
time_t t; time( &t );
|
||||
struct tm date_tm;
|
||||
localtime_s( &date_tm, &t );
|
||||
|
||||
_snprintf_s( timestamp, _TRUNCATE, 63,
|
||||
"[%4d-%02d-%02d %02d:%02d:%02d]: ",
|
||||
( date_tm.tm_year + 1900 ),
|
||||
( date_tm.tm_mon + 1 ),
|
||||
date_tm.tm_mday,
|
||||
date_tm.tm_hour,
|
||||
date_tm.tm_min,
|
||||
date_tm.tm_sec );
|
||||
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
void Log::CheckFileStatus( LOG_TYPE type )
|
||||
{
|
||||
time_t t; time( &t );
|
||||
struct tm date_tm;
|
||||
localtime_s( &date_tm, &t );
|
||||
|
||||
if( current_open_hour != date_tm.tm_hour )
|
||||
{
|
||||
current_open_hour = date_tm.tm_hour;
|
||||
if( file_stream[ type ].is_open() )
|
||||
{
|
||||
file_stream[ type ].close();
|
||||
}
|
||||
}
|
||||
|
||||
if( !file_stream[ type ].is_open() )
|
||||
{
|
||||
char szFileName[ 256 ] = "";
|
||||
|
||||
_snprintf_s( szFileName, _TRUNCATE, 255, "./log/%s/log_%04d.%02d.%02d_%02d.log",
|
||||
LOG_PATH[ type ],
|
||||
( date_tm.tm_year + 1900 ),
|
||||
( date_tm.tm_mon + 1 ),
|
||||
date_tm.tm_mday,
|
||||
date_tm.tm_hour );
|
||||
|
||||
file_stream[ type ].open( szFileName, std::fstream::out | std::fstream::app );
|
||||
}
|
||||
}
|
||||
|
||||
void Log::WriteToLog( LOG_TYPE type, const std::string &message )
|
||||
{
|
||||
std::lock_guard lock( log_lock );
|
||||
|
||||
HANDLE hConsole = GetStdHandle( STD_OUTPUT_HANDLE );
|
||||
SetConsoleTextAttribute( hConsole, type + 10 );
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case log_generic: printf( "[INFO]: " ); break;
|
||||
case log_debug: printf( "[DEBUG]: " ); break;
|
||||
case log_warn: printf( "[WARN]: " ); break;
|
||||
case log_error: printf( "[ERROR]: " ); break;
|
||||
}
|
||||
|
||||
SetConsoleTextAttribute( hConsole, 15 );
|
||||
printf( "%s\n", message.c_str() );
|
||||
|
||||
CheckFileStatus( type );
|
||||
file_stream[ type ] << GetTimeStamp() << message << '\n';
|
||||
}
|
||||
|
||||
void Log::Packet( std::vector<uint8_t> p, size_t size, bool send )
|
||||
{
|
||||
#ifndef _DEBUG
|
||||
return;
|
||||
#endif
|
||||
|
||||
std::lock_guard<std::mutex> lock( log_lock );
|
||||
|
||||
HANDLE hConsole = GetStdHandle( STD_OUTPUT_HANDLE );
|
||||
|
||||
SetConsoleTextAttribute( hConsole, 15 );
|
||||
|
||||
uint16_t i = 0;
|
||||
uint8_t line[ 16 ] = {};
|
||||
uint8_t r = 0;
|
||||
|
||||
SetConsoleTextAttribute( hConsole, send ? 11 : 10 );
|
||||
printf( "(%s)(00|01|02|03|04|05|06|07|08|09|0A|0B|0C|0D|0E|0F)\n", send ? "SEND" : "RECV" );
|
||||
SetConsoleTextAttribute( hConsole, 15 );
|
||||
|
||||
while( i < size )
|
||||
{
|
||||
if( i % 16 == 0 )
|
||||
{
|
||||
if( i > 0 )
|
||||
{
|
||||
// Print ASCII characters for the previous line
|
||||
printf( " " );
|
||||
for( uint8_t j = 0; j < r; ++j )
|
||||
{
|
||||
char c = line[ j ];
|
||||
printf( "%c", ( c >= 0x20 && c <= 0x7E ) ? c : '.' );
|
||||
}
|
||||
printf( "\n" );
|
||||
}
|
||||
printf( "(%04X) ", i );
|
||||
r = 0;
|
||||
}
|
||||
|
||||
line[ r++ ] = p[ i ];
|
||||
|
||||
// Highlight packet type or flags
|
||||
if( i == 0 || i == 1 )
|
||||
{
|
||||
SetConsoleTextAttribute( hConsole, send ? 11 : 10 );
|
||||
printf( "%02X ", p[ i ] );
|
||||
SetConsoleTextAttribute( hConsole, 15 );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "%02X ", p[ i ] );
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
// Padding for incomplete final line
|
||||
if( r > 0 )
|
||||
{
|
||||
for( uint8_t j = r; j < 16; ++j )
|
||||
printf( " " ); // Pad to align character section
|
||||
|
||||
printf( " " );
|
||||
for( uint8_t j = 0; j < r; ++j )
|
||||
{
|
||||
char c = line[ j ];
|
||||
printf( "%c", ( c >= 0x20 && c <= 0x7E ) ? c : '.' );
|
||||
}
|
||||
}
|
||||
|
||||
printf( "\n\n" );
|
||||
}
|
||||
|
||||
void Log::ToFile( std::string prefix, std::vector<uint8_t> p, size_t size )
|
||||
{
|
||||
static char timestamp[ 64 ] = "";
|
||||
|
||||
time_t t; time( &t );
|
||||
struct tm date_tm;
|
||||
localtime_s( &date_tm, &t );
|
||||
|
||||
_snprintf_s( timestamp, _TRUNCATE, 63,
|
||||
"%02d-%02d-%02d.packet.bin",
|
||||
date_tm.tm_hour,
|
||||
date_tm.tm_min,
|
||||
date_tm.tm_sec );
|
||||
|
||||
std::string filename = prefix + '_' + timestamp;
|
||||
|
||||
std::fstream file;
|
||||
file.open( filename, std::ios::out | std::ios::binary );
|
||||
|
||||
if( !file.is_open() )
|
||||
{
|
||||
Error( "Failed to open packet log file: %s", filename.c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
file.write( reinterpret_cast< const char * >( p.data() ), size );
|
||||
file.close();
|
||||
}
|
||||
100
Source/main.cpp
Normal file
100
Source/main.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "logging.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "Database/Database.hpp"
|
||||
#include "Lobby Server/LobbyServer.hpp"
|
||||
#include "Discovery Server/DiscoveryServer.hpp"
|
||||
|
||||
std::atomic< bool > g_isRunning( true );
|
||||
|
||||
static void SignalHandler( int signal )
|
||||
{
|
||||
if( signal == SIGINT || signal == SIGTERM )
|
||||
{
|
||||
g_isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void ShowStartup()
|
||||
{
|
||||
printf
|
||||
(
|
||||
"------------------------------------------------------\n"
|
||||
"Champions Reborn | Server Build Version %s\n"
|
||||
"------------------------------------------------------\n\n",
|
||||
__DATE__
|
||||
);
|
||||
}
|
||||
|
||||
static bool NetworkStartup()
|
||||
{
|
||||
WORD wVersionRequest = MAKEWORD( 2, 2 );
|
||||
WSADATA wsaData;
|
||||
|
||||
if( WSAStartup( wVersionRequest, &wsaData ) != 0 )
|
||||
{
|
||||
Log::Error( "WSAStartup() failed" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ShowStartup();
|
||||
|
||||
std::signal( SIGINT, SignalHandler );
|
||||
std::signal( SIGTERM, SignalHandler );
|
||||
|
||||
Log::Info( "Server Start..." );
|
||||
|
||||
if( !NetworkStartup() )
|
||||
{
|
||||
Log::Error( "Could not initialize network." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !Config::Load( "config.ini" ) )
|
||||
{
|
||||
Log::Error( "Failed to load configuration file." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &lobby_server = LobbyServer::Get();
|
||||
lobby_server.Start( Config::service_ip );
|
||||
|
||||
auto &discovery_server = DiscoveryServer::Get();
|
||||
discovery_server.Start( Config::service_ip, Config::discovery_port );
|
||||
|
||||
auto &database = Database::Get();
|
||||
|
||||
while( g_isRunning )
|
||||
{
|
||||
if( !lobby_server.isRunning() )
|
||||
{
|
||||
Log::Error( "Lobby Server is not running. Exiting." );
|
||||
break;
|
||||
}
|
||||
|
||||
if( !discovery_server.isRunning() )
|
||||
{
|
||||
Log::Error( "Discovery Server is not running. Exiting." );
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
|
||||
}
|
||||
|
||||
Log::Info( "Shutting down servers..." );
|
||||
|
||||
lobby_server.Stop();
|
||||
discovery_server.Stop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
8
Source/stdafx.cpp
Normal file
8
Source/stdafx.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// Master Server.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
Reference in New Issue
Block a user