mirror of
https://github.com/HikikoMarmy/Champions-Reborn-Server.git
synced 2026-04-05 08:59:54 -03:00
Discovery for game clients.
This commit is contained in:
@@ -28,12 +28,20 @@ void DiscoveryServer::Start( std::string ip, int32_t port )
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr_in serverInfo;
|
||||
serverInfo.sin_family = AF_INET;
|
||||
serverInfo.sin_addr.s_addr = ADDR_ANY;// inet_addr( ip.c_str() );
|
||||
serverInfo.sin_port = htons( port );
|
||||
sockaddr_in service;
|
||||
service.sin_family = AF_INET;
|
||||
service.sin_port = htons( port );
|
||||
|
||||
if( bind( m_socket, ( SOCKADDR * )&serverInfo, sizeof( serverInfo ) ) == SOCKET_ERROR )
|
||||
if (ip == "0.0.0.0")
|
||||
{
|
||||
service.sin_addr.s_addr = ADDR_ANY;
|
||||
}
|
||||
else
|
||||
{
|
||||
service.sin_addr.s_addr = inet_addr( ip.c_str() );
|
||||
}
|
||||
|
||||
if( bind( m_socket, ( SOCKADDR * )&service, sizeof( service ) ) == SOCKET_ERROR )
|
||||
{
|
||||
Log::Error( "Failed to bind socket." );
|
||||
closesocket( m_socket );
|
||||
@@ -57,86 +65,61 @@ void DiscoveryServer::Stop()
|
||||
|
||||
void DiscoveryServer::Run()
|
||||
{
|
||||
FD_SET readSet;
|
||||
timeval timeout = { 0, 1000 };
|
||||
sockaddr_in clientAddr;
|
||||
int clientAddrLen = sizeof(clientAddr);
|
||||
|
||||
while( m_running )
|
||||
{
|
||||
FD_ZERO( &readSet );
|
||||
FD_SET( m_socket, &readSet );
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
auto result = select( 0, &readSet, nullptr, nullptr, &timeout );
|
||||
auto bytesReceived = recvfrom(m_socket, (char*)m_recvBuffer.data(), 1024, 0, (struct sockaddr*)&clientAddr, &clientAddrLen);
|
||||
|
||||
if( result == SOCKET_ERROR )
|
||||
if (bytesReceived == SOCKET_ERROR || bytesReceived < 4)
|
||||
{
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( FD_ISSET( m_socket, &readSet ) )
|
||||
{
|
||||
ReadSocket();
|
||||
}
|
||||
ProcessPacket(&clientAddr, std::make_shared< ByteStream >(m_recvBuffer.data(), bytesReceived));
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryServer::UpdateDiscoveryInfo( std::wstring sessionId, std::string ip, int32_t port )
|
||||
void DiscoveryServer::ProcessPacket(sockaddr_in* clientAddr, sptr_byte_stream stream)
|
||||
{
|
||||
if( sessionId.empty() || sessionId.size() != 16 )
|
||||
Log::Packet( stream->data, stream->data.size(), false );
|
||||
|
||||
auto head = stream->read_u32();
|
||||
|
||||
if (0x20 == head)
|
||||
{
|
||||
Log::Error( "Invalid session id." );
|
||||
return;
|
||||
ProcessDiscoveryInitial(clientAddr, stream);
|
||||
}
|
||||
|
||||
m_userDiscoveryInfo[ sessionId ] = { ip, port, GetTickCount64() };
|
||||
auto user = RealmUserManager::Get().GetUserByAddress(clientAddr);
|
||||
|
||||
Log::Debug( "UpdateDiscoveryInfo : [%S] %s:%d", sessionId.c_str(), ip.c_str(), port );
|
||||
}
|
||||
|
||||
std::optional<UserDiscoveryInfo> DiscoveryServer::GetDiscoveryInfo( const std::wstring &sessionId ) const
|
||||
{
|
||||
auto it = m_userDiscoveryInfo.find( sessionId );
|
||||
if( it == m_userDiscoveryInfo.end() )
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void DiscoveryServer::ReadSocket()
|
||||
{
|
||||
sockaddr_in clientAddr;
|
||||
int clientAddrLen = sizeof( clientAddr );
|
||||
|
||||
// Receive data from the client
|
||||
auto bytesReceived = recvfrom( m_socket, ( char * )m_recvBuffer.data(), ( int )m_recvBuffer.size(), 0, ( struct sockaddr * )&clientAddr, &clientAddrLen );
|
||||
|
||||
if( bytesReceived == SOCKET_ERROR )
|
||||
if (nullptr == user)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( bytesReceived == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
stream->set_position(0);
|
||||
|
||||
if( bytesReceived > 0 )
|
||||
if (user->is_host)
|
||||
{
|
||||
HandleDiscoveryUpdate( &clientAddr, std::make_shared< ByteStream >( m_recvBuffer.data(), bytesReceived ) );
|
||||
ProcessHost(user, stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessJoin(user, stream);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryServer::HandleDiscoveryUpdate( sockaddr_in *clientAddr, sptr_byte_stream stream )
|
||||
void DiscoveryServer::ProcessDiscoveryInitial( sockaddr_in *clientAddr, sptr_byte_stream stream )
|
||||
{
|
||||
if( stream->get_length() != 52 )
|
||||
{
|
||||
Log::Error( "Invalid discovery update." );
|
||||
return;
|
||||
}
|
||||
auto encryptedBytes = stream->read_bytes( 0x20 );
|
||||
auto decryptedBytes = RealmCrypt::decryptSymmetric(encryptedBytes);
|
||||
|
||||
auto sessionId = stream->read_encrypted_utf16( false );
|
||||
std::wstring sessionId(16, L'\0');
|
||||
std::memcpy(sessionId.data(), decryptedBytes.data(), 32);
|
||||
|
||||
if( sessionId.empty() || sessionId.size() != 16 )
|
||||
{
|
||||
@@ -145,29 +128,270 @@ void DiscoveryServer::HandleDiscoveryUpdate( sockaddr_in *clientAddr, sptr_byte_
|
||||
}
|
||||
|
||||
auto remoteIp = inet_ntoa( clientAddr->sin_addr );
|
||||
auto remotePort = clientAddr->sin_port;
|
||||
auto remotePort = ntohs( clientAddr->sin_port );
|
||||
|
||||
//RealmUserManager::Get().UpdateUserDiscoveryInfo( sessionId, remoteIp, remotePort );
|
||||
auto user = RealmUserManager::Get().GetUser(sessionId);
|
||||
if (user == nullptr)
|
||||
{
|
||||
Log::Error("User not found! [%S]", sessionId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateDiscoveryInfo( sessionId, remoteIp, remotePort );
|
||||
if (remoteIp != user->tcp->remote_ip)
|
||||
{
|
||||
Log::Error( "Discovery Handshake from invalid IP!" );
|
||||
return;
|
||||
}
|
||||
|
||||
user->udp->fd = m_socket;
|
||||
user->udp->remote_addr = *clientAddr;
|
||||
user->udp->remote_ip = remoteIp;
|
||||
user->udp->remote_port = remotePort;
|
||||
|
||||
user->discovery_addr = remoteIp;
|
||||
user->discovery_port = remotePort;
|
||||
|
||||
//Log::Debug( "New Discovery Connection : [%S] %s:%d", sessionId.c_str(), remoteIp, remotePort );
|
||||
}
|
||||
|
||||
/*
|
||||
void DiscoveryServer::SendDiscoveryClientHandshake( sptr_discovery_record record, sptr_byte_stream stream )
|
||||
void DiscoveryServer::ProcessHost(sptr_user user, sptr_byte_stream stream)
|
||||
{
|
||||
ByteStream response;
|
||||
response.write_u8( 0x07 ); // REALM_SERVER_CLIENT_INITIAL
|
||||
static int cmd = 0;
|
||||
switch (user->discovery_state)
|
||||
{
|
||||
case DiscoveryState::Initial_Connect:
|
||||
{
|
||||
if (false == GameSessionManager::Get().SetGameOpen(user))
|
||||
{
|
||||
Log::Error("Failed to set game open! [%S]", user->session_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
record->m_socket->send( response );
|
||||
user->discovery_state = DiscoveryState::Host_Waiting;
|
||||
|
||||
user->udp->send(stream);
|
||||
} break;
|
||||
|
||||
case DiscoveryState::Host_Waiting:
|
||||
{
|
||||
Send_Tmp(&user->udp->remote_addr);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryServer::SendDiscoveryClientPing( sptr_discovery_record record, sptr_byte_stream stream )
|
||||
void DiscoveryServer::ProcessJoin(sptr_user user, sptr_byte_stream stream)
|
||||
{
|
||||
auto remoteIp = stream->read_sz_utf8();
|
||||
switch (user->discovery_state)
|
||||
{
|
||||
case DiscoveryState::Initial_Connect:
|
||||
{
|
||||
if (false == GameSessionManager::Get().JoinGame(user))
|
||||
{
|
||||
Log::Error("Failed to join game session! [%S]", user->session_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ByteStream response;
|
||||
response.write_u8( 0x08 ); // REALM_SERVER_CLIENT_PING
|
||||
user->discovery_state = DiscoveryState::Negotiating;
|
||||
} break;
|
||||
|
||||
record->m_socket->send( response );
|
||||
case DiscoveryState::Negotiating:
|
||||
{
|
||||
// Initialize Realm Server Client
|
||||
Send_07(user);
|
||||
user->discovery_state = DiscoveryState::Joining;
|
||||
} break;
|
||||
|
||||
case DiscoveryState::Joining:
|
||||
{
|
||||
if (false == Process_0C(user, stream))
|
||||
{
|
||||
Log::Error("Failed to parse user character data...");
|
||||
return;
|
||||
}
|
||||
|
||||
user->discovery_state = DiscoveryState::Joined;
|
||||
} break;
|
||||
|
||||
case DiscoveryState::Joined:
|
||||
{
|
||||
Send_08(user);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void DiscoveryServer::PrepareJoinGame(sptr_user user)
|
||||
{
|
||||
if (false == GameSessionManager::Get().JoinGame(user))
|
||||
{
|
||||
|
||||
Log::Error("Failed to join game session! [%S]", user->session_id.c_str());
|
||||
//return;
|
||||
}
|
||||
|
||||
// Realm Server Client Initialize
|
||||
Send_07( user );
|
||||
|
||||
user->discovery_state = DiscoveryState::Initial_Connect;
|
||||
}
|
||||
|
||||
void DiscoveryServer::PrepareHostGame(sptr_user user)
|
||||
{
|
||||
if (false == GameSessionManager::Get().SetGameOpen(user))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user->discovery_state = DiscoveryState::Host_Waiting;
|
||||
}
|
||||
|
||||
void DiscoveryServer::Send_07( sptr_user user )
|
||||
{
|
||||
ByteStream reply;
|
||||
reply.write_u32(0x07);
|
||||
|
||||
user->udp->send(reply);
|
||||
}
|
||||
|
||||
void DiscoveryServer::Send_08( sptr_user user )
|
||||
{
|
||||
auto session = GameSessionManager::Get().FindGame(user->game_id);
|
||||
|
||||
if (nullptr == session)
|
||||
{
|
||||
Log::Error("Game session not found! [%d]", user->game_id);
|
||||
return;
|
||||
}
|
||||
|
||||
ByteStream reply;
|
||||
{
|
||||
reply.write_u32(0x08);
|
||||
reply.write_sz_utf8("", 24);
|
||||
|
||||
reply.write_sz_utf8(std::to_string(session->m_userList.size()));
|
||||
reply.write_sz_utf8("4");
|
||||
|
||||
for (auto& member : session->m_userList)
|
||||
{
|
||||
reply.write_sz_utf8(member->player_name, 24);
|
||||
reply.write_sz_utf8(member->player_class, 24);
|
||||
reply.write_u32(member->player_level);
|
||||
}
|
||||
}
|
||||
|
||||
for( auto &member : session->m_userList )
|
||||
{
|
||||
if (member->is_host)
|
||||
continue;
|
||||
|
||||
if( !member->is_ready )
|
||||
continue;
|
||||
|
||||
member->udp->send(reply);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryServer::Send_09( sptr_user user )
|
||||
{
|
||||
auto session = GameSessionManager::Get().FindGame( user->game_id );
|
||||
|
||||
if( nullptr == session)
|
||||
{
|
||||
Log::Error( "Game session not found! [%d]", user->game_id);
|
||||
return;
|
||||
}
|
||||
|
||||
ByteStream reply;
|
||||
{
|
||||
reply.write_u32(0x09);
|
||||
|
||||
reply.write_u32(session->m_userList.size());
|
||||
|
||||
for( auto &member : session->m_userList )
|
||||
{
|
||||
reply.write_sz_utf8(member->udp->remote_ip, 24);
|
||||
reply.write_u32(member->udp->remote_port);
|
||||
}
|
||||
}
|
||||
|
||||
for( auto &member : session->m_userList )
|
||||
{
|
||||
member->udp->send(reply);
|
||||
}
|
||||
}
|
||||
|
||||
bool DiscoveryServer::Process_0C( sptr_user user, sptr_byte_stream stream)
|
||||
{
|
||||
if (0x0C != stream->read_u32())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Log::Packet(stream->data, stream->data.size(), false);
|
||||
|
||||
if (nullptr == user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
user->local_addr = stream->read_utf8(24);
|
||||
user->discovery_addr = stream->read_utf8(24);
|
||||
user->discovery_port = stream->read_u32();
|
||||
|
||||
user->player_name = stream->read_utf8(24);
|
||||
user->player_class = stream->read_utf8(24);
|
||||
user->player_level = stream->read_u32();
|
||||
|
||||
Log::Debug("Join User Info : %s %s %d", user->player_name.c_str(), user->player_class.c_str(), user->player_level);
|
||||
|
||||
user->is_ready = true;
|
||||
|
||||
Send_08(user);
|
||||
Send_0C(user);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DiscoveryServer::Send_0C( sptr_user user )
|
||||
{
|
||||
auto session = GameSessionManager::Get().FindGame(user->game_id);
|
||||
|
||||
if (nullptr == session)
|
||||
{
|
||||
Log::Error("Game session not found! [%d]", user->game_id);
|
||||
return;
|
||||
}
|
||||
|
||||
auto host = session->GetHost();
|
||||
|
||||
ByteStream reply;
|
||||
{
|
||||
reply.write_u32(0x0C);
|
||||
|
||||
reply.write_sz_utf8(user->local_addr, 24);
|
||||
reply.write_sz_utf8(user->discovery_addr, 24);
|
||||
reply.write_u32(user->discovery_port);
|
||||
|
||||
reply.write_sz_utf8(host->player_name, 24);
|
||||
reply.write_sz_utf8(host->player_class, 24);
|
||||
reply.write_u32(host->player_level);
|
||||
}
|
||||
host->udp->send(reply);
|
||||
}
|
||||
|
||||
void DiscoveryServer::Send_0D( sptr_user user )
|
||||
{
|
||||
// Send Reply to clientAddr
|
||||
std::vector< uint8_t > reply = { 0x0D, 0x00, 0x00, 0x00 };
|
||||
}
|
||||
|
||||
void DiscoveryServer::Send_Tmp(sockaddr_in* clientAddr)
|
||||
{
|
||||
ByteStream reply;
|
||||
{
|
||||
reply.write_u32(0x08);
|
||||
reply.write_sz_utf8("193.149.164.146", 24);
|
||||
reply.write_u32(10101);
|
||||
}
|
||||
|
||||
sendto(m_socket, (char*)reply.data.data(), reply.data.size(), 0, (struct sockaddr*)clientAddr, sizeof(sockaddr_in));
|
||||
}
|
||||
@@ -4,16 +4,12 @@
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "DiscoverySession.h"
|
||||
|
||||
struct UserDiscoveryInfo {
|
||||
std::string m_ip;
|
||||
int32_t m_port;
|
||||
uint64_t m_lastUpdateTime;
|
||||
};
|
||||
|
||||
class DiscoveryServer
|
||||
{
|
||||
private:
|
||||
static inline std::unique_ptr< DiscoveryServer > m_instance;
|
||||
static inline std::mutex m_mutex;
|
||||
|
||||
public:
|
||||
static DiscoveryServer& Get()
|
||||
{
|
||||
@@ -39,21 +35,31 @@ public:
|
||||
return m_running;
|
||||
}
|
||||
|
||||
void UpdateDiscoveryInfo( std::wstring sessionId, std::string ip, int32_t port );
|
||||
std::optional<UserDiscoveryInfo> GetDiscoveryInfo( const std::wstring &sessionId ) const;
|
||||
|
||||
private:
|
||||
void ReadSocket();
|
||||
void HandleDiscoveryUpdate( sockaddr_in *clientAddr, sptr_byte_stream stream );
|
||||
void ProcessPacket(sockaddr_in* clientAddr, sptr_byte_stream stream);
|
||||
|
||||
static inline std::unique_ptr< DiscoveryServer > m_instance;
|
||||
static inline std::mutex m_mutex;
|
||||
void ProcessDiscoveryInitial( sockaddr_in *clientAddr, sptr_byte_stream stream );
|
||||
|
||||
void ProcessHost(sptr_user user, sptr_byte_stream stream);
|
||||
|
||||
// Joinee Handlers
|
||||
void ProcessJoin(sptr_user user, sptr_byte_stream stream);
|
||||
void PrepareJoinGame( sptr_user user );
|
||||
void PrepareHostGame( sptr_user user );
|
||||
|
||||
void Send_07( sptr_user user ); // Realm Initialize
|
||||
void Send_08( sptr_user user ); // Member Info Update
|
||||
void Send_09( sptr_user user ); // Member Address Info / Start Game
|
||||
|
||||
bool Process_0C( sptr_user user, sptr_byte_stream stream ); // Member Character Information
|
||||
void Send_0C(sptr_user user); // Joining Member Character Info
|
||||
void Send_0D(sptr_user user);
|
||||
|
||||
void Send_Tmp(sockaddr_in* clientAddr);
|
||||
|
||||
std::atomic< bool > m_running;
|
||||
std::thread m_thread;
|
||||
|
||||
SOCKET m_socket;
|
||||
std::vector< uint8_t > m_recvBuffer;
|
||||
|
||||
std::unordered_map< std::wstring, UserDiscoveryInfo > m_userDiscoveryInfo;
|
||||
std::vector< unsigned char > m_recvBuffer;
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <string>
|
||||
Reference in New Issue
Block a user