14#if PROTOCOL_VERSION > 758
34 name = login_or_microsoft_cache_key_or_mc_token;
38 if (!
authentifier->AuthMicrosoft(login_or_microsoft_cache_key_or_mc_token))
40 throw std::runtime_error(
"Error trying to authenticate on Mojang server using Microsoft auth flow");
46 if (!
authentifier->AuthMCToken(login_or_microsoft_cache_key_or_mc_token))
48 throw std::runtime_error(
"Error trying to authenticate on Mojang server using provided mc token");
61 state = ConnectionState::Handshake;
70 return com->IsInitialized();
73 std::shared_ptr<ServerboundClientIntentionPacket> handshake_packet = std::make_shared<ServerboundClientIntentionPacket>();
74 handshake_packet->SetProtocolVersion(PROTOCOL_VERSION);
75 handshake_packet->SetHostName(
com->GetIp());
76 handshake_packet->SetPort(
com->GetPort());
77 handshake_packet->SetIntention(
static_cast<int>(ConnectionState::Login));
78 Send(handshake_packet);
80 state = ConnectionState::Login;
82 std::shared_ptr<ServerboundHelloPacket> loginstart_packet = std::make_shared<ServerboundHelloPacket>();
83#if PROTOCOL_VERSION < 759
84 loginstart_packet->SetGameProfile(
name);
86 loginstart_packet->SetName_(
name);
89#if PROTOCOL_VERSION < 761
95 loginstart_packet->SetPublicKey(key);
99#if PROTOCOL_VERSION > 759
100 loginstart_packet->SetProfileId(
authentifier->GetPlayerUUID());
104 Send(loginstart_packet);
109 state = constant_connection_state;
120 state = ConnectionState::None;
148 std::vector<unsigned char> packet_data;
149 packet_data.reserve(256);
150 packet->Write(packet_data);
153 com->SendPacket(packet_data);
157#ifdef USE_COMPRESSION
160 packet_data.insert(packet_data.begin(), 0x00);
161 com->SendPacket(packet_data);
165 std::vector<unsigned char> compressed_packet;
166 compressed_packet.reserve(packet_data.size() + 5);
167 WriteData<VarInt>(
static_cast<int>(packet_data.size()), compressed_packet);
168 std::vector<unsigned char> compressed_data =
Compress(packet_data);
169 compressed_packet.insert(compressed_packet.end(), compressed_data.begin(), compressed_data.end());
170 com->SendPacket(compressed_packet);
173 throw std::runtime_error(
"Program compiled without ZLIB. Cannot send compressed message");
191#if PROTOCOL_VERSION > 758
192 if (message[0] ==
'/')
194 LOG_INFO(
"You're trying to send a message starting with '/'. Use SendChatCommand instead if you want the server to interprete it as a command.");
198 std::shared_ptr<ServerboundChatPacket> chat_message = std::make_shared<ServerboundChatPacket>();
199 chat_message->SetMessage(message);
200#if PROTOCOL_VERSION > 758
201#if PROTOCOL_VERSION < 761
202 chat_message->SetSignedPreview(
false);
206 long long int salt, timestamp;
207 std::vector<unsigned char> signature;
208#if PROTOCOL_VERSION == 759
210 signature =
authentifier->GetMessageSignature(message, salt, timestamp);
211#elif PROTOCOL_VERSION == 760
217 signature =
authentifier->GetMessageSignature(message,
chat_context.GetLastSignature(), last_seen_update.GetLastSeen(), salt, timestamp);
221 chat_message->SetLastSeenMessages(last_seen_update);
227 chat_message->SetLastSeenMessages(updates);
229 if (signature.empty())
231 throw std::runtime_error(
"Empty chat message signature.");
233 chat_message->SetTimestamp(timestamp);
235#if PROTOCOL_VERSION < 760
237 salt_signature.SetSalt(salt);
238 salt_signature.SetSignature(signature);
240 chat_message->SetSaltSignature(salt_signature);
242 chat_message->SetSalt(salt);
243 chat_message->SetSignature(signature);
249 chat_message->SetTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
257#if PROTOCOL_VERSION > 765
260 std::shared_ptr<ServerboundChatCommandPacket> chat_command = std::make_shared<ServerboundChatCommandPacket>();
261 chat_command->SetCommand(command);
266#if PROTOCOL_VERSION > 758
267#if PROTOCOL_VERSION > 765
268 std::shared_ptr<ServerboundChatCommandSignedPacket> chat_command = std::make_shared<ServerboundChatCommandSignedPacket>();
270 std::shared_ptr<ServerboundChatCommandPacket> chat_command = std::make_shared<ServerboundChatCommandPacket>();
272 chat_command->SetCommand(command);
273 chat_command->SetTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
274#if PROTOCOL_VERSION > 759
275 std::mt19937 rnd(
static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count()));
276 chat_command->SetSalt(std::uniform_int_distribution<long long int>(std::numeric_limits<long long int>::min(), std::numeric_limits<long long int>::max())(rnd));
278#if PROTOCOL_VERSION < 761
279 chat_command->SetSignedPreview(
false);
281#if PROTOCOL_VERSION == 760
288 chat_command->SetLastSeenMessages(last_seen_update);
289#elif PROTOCOL_VERSION > 760
291 chat_command->SetLastSeenMessages(updates);
294 chat_command->SetArgumentSignatures({});
296 std::shared_ptr<ServerboundChatPacket> chat_command = std::make_shared<ServerboundChatPacket>();
297 chat_command->SetMessage(
"/" + command);
312 while (
state != ConnectionState::None)
320 std::vector<unsigned char> packet;
329 if (packet.size() > 0)
337#ifdef USE_COMPRESSION
338 size_t length = packet.size();
340 int data_length = ReadData<VarInt>(iter, length);
343 if (data_length == 0)
346 packet.erase(packet.begin());
352 const int size_varint =
static_cast<int>(packet.size() - length);
354 std::vector<unsigned char> uncompressed_packet =
Decompress(packet, size_varint);
358 throw std::runtime_error(
"Program compiled without USE_COMPRESSION. Cannot read compressed message");
365 catch (
const std::exception& e)
384 std::vector<unsigned char>::const_iterator packet_iterator = bytes.begin();
385 size_t length = bytes.size();
387 const int packet_id = ReadData<VarInt>(packet_iterator, length);
391 if (packet !=
nullptr)
395 packet->Read(packet_iterator, length);
397 catch (
const std::exception& e)
399 LOG_FATAL(
"Parsing exception while parsing message \"" << packet->GetName() <<
"\"\n" << e.what());
402 for (
size_t i = 0; i <
subscribed.size(); i++)
424#if PROTOCOL_VERSION < 768
430#if PROTOCOL_VERSION < 764
431 state = ConnectionState::Play;
433 state = ConnectionState::Configuration;
434 std::shared_ptr<ServerboundLoginAcknowledgedPacket> login_ack_packet = std::make_shared<ServerboundLoginAcknowledgedPacket>();
435 Send(login_ack_packet);
437 std::shared_ptr<ServerboundCustomPayloadConfigurationPacket> custom_payload_packet = std::make_shared<ServerboundCustomPayloadConfigurationPacket>();
438 custom_payload_packet->SetIdentifier(
"minecraft:brand");
439 std::vector<unsigned char> payload;
440 WriteData<std::string>(
"vanilla", payload);
441 custom_payload_packet->SetRawData(payload);
442 Send(custom_payload_packet);
444 std::shared_ptr<ServerboundClientInformationConfigurationPacket> client_info_packet = std::make_shared<ServerboundClientInformationConfigurationPacket>();
446 info.SetLanguage(
"fr_FR");
447 info.SetViewDistance(10);
449 info.SetChatColors(
true);
450 info.SetModelCustomisation(0xFF);
452#if PROTOCOL_VERSION > 767
453 info.SetParticleStatus(2);
455 client_info_packet->SetClientInformation(info);
457 Send(client_info_packet);
464#
if PROTOCOL_VERSION > 765
465 && packet.GetShouldAuthenticate()
469 throw std::runtime_error(
"Authentication asked while no valid account has been provided, make sure to connect with a valid Microsoft Account, or to a server with online-mode=false");
473 std::shared_ptr<AESEncrypter> encrypter = std::make_shared<AESEncrypter>();
475 std::vector<unsigned char> raw_shared_secret;
476 std::vector<unsigned char> encrypted_shared_secret;
478#if PROTOCOL_VERSION < 759
479 std::vector<unsigned char> encrypted_nonce;
480 encrypter->Init(packet.GetPublicKey(), packet.GetNonce(),
481 raw_shared_secret, encrypted_nonce, encrypted_shared_secret);
482#elif PROTOCOL_VERSION < 761
483 std::vector<unsigned char> salted_nonce_signature;
485 encrypter->Init(packet.GetPublicKey(), packet.GetNonce(),
authentifier->GetPrivateKey(),
486 raw_shared_secret, encrypted_shared_secret,
487 salt, salted_nonce_signature);
489 std::vector<unsigned char> encrypted_challenge;
490 encrypter->Init(packet.GetPublicKey(), packet.GetChallenge(),
491 raw_shared_secret, encrypted_shared_secret,
492 encrypted_challenge);
496#if PROTOCOL_VERSION > 765
497 if (packet.GetShouldAuthenticate())
500 authentifier->JoinServer(packet.GetServerId(), raw_shared_secret, packet.GetPublicKey());
503 std::shared_ptr<ServerboundKeyPacket> response_packet = std::make_shared<ServerboundKeyPacket>();
504 response_packet->SetKeyBytes(encrypted_shared_secret);
506#if PROTOCOL_VERSION < 759
508 response_packet->SetNonce(encrypted_nonce);
509#elif PROTOCOL_VERSION < 761
512 salt_signature.SetSalt(salt);
513 salt_signature.SetSignature(salted_nonce_signature);
514 response_packet->SetSaltSignature(salt_signature);
517 response_packet->SetEncryptedChallenge(encrypted_challenge);
520 Send(response_packet);
523 com->SetEncrypter(encrypter);
525 throw std::runtime_error(
"Your version of botcraft doesn't support encryption. Either run your server with online-mode=false or recompile botcraft");
531 std::shared_ptr<ServerboundKeepAlivePacket> keep_alive_packet = std::make_shared<ServerboundKeepAlivePacket>();
532 keep_alive_packet->SetId_(packet.GetId_());
533 Send(keep_alive_packet);
536#if PROTOCOL_VERSION > 754
539 std::shared_ptr<ServerboundPongPacket> pong_packet = std::make_shared<ServerboundPongPacket>();
540 pong_packet->SetId_(packet.GetId_());
545#if PROTOCOL_VERSION > 340
551#if PROTOCOL_VERSION > 753
552 if (packet.GetIdentifier().GetFull() ==
"fabric-networking-api-v1:early_registration")
554#if PROTOCOL_VERSION < 764
555 std::shared_ptr<ServerboundCustomQueryPacket> custom_query_anwser = std::make_shared<ServerboundCustomQueryPacket>();
556 custom_query_anwser->SetTransactionId(packet.GetTransactionId());
557 custom_query_anwser->SetData(std::nullopt);
559 std::shared_ptr<ServerboundCustomQueryAnswerPacket> custom_query_anwser = std::make_shared<ServerboundCustomQueryAnswerPacket>();
560 custom_query_anwser->SetTransactionId(packet.GetTransactionId());
561 custom_query_anwser->SetPayload(std::nullopt);
563 Send(custom_query_anwser);
570#if PROTOCOL_VERSION > 759
573#if PROTOCOL_VERSION < 761
575#if _MSC_VER || __MINGW32__
576#pragma push_macro("GetMessage")
580#if _MSC_VER || __MINGW32__
581#pragma pop_macro("GetMessage")
584 if (packet.GetSignature().has_value())
588 chat_context.
AddSeenMessage(std::vector<unsigned char>(packet.GetSignature().value().begin(), packet.GetSignature().value().end()));
592 std::shared_ptr<ServerboundChatAckPacket> ack_packet = std::make_shared<ServerboundChatAckPacket>();
600#if PROTOCOL_VERSION < 761
610#if PROTOCOL_VERSION < 764
613 std::shared_ptr<ServerboundCustomPayloadPacket> custom_payload_packet = std::make_shared<ServerboundCustomPayloadPacket>();
614 custom_payload_packet->SetIdentifier(
"minecraft:brand");
615 std::vector<unsigned char> payload;
616 WriteData<std::string>(
"vanilla", payload);
617 custom_payload_packet->SetRawData(payload);
618 Send(custom_payload_packet);
621#if PROTOCOL_VERSION > 760
624 std::shared_ptr<ServerboundChatSessionUpdatePacket> chat_session_packet = std::make_shared<ServerboundChatSessionUpdatePacket>();
632 chat_session_data.SetProfilePublicKey(key);
634 std::mt19937 rnd = std::mt19937(
static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count()));
635 std::uniform_int_distribution<int> distrib(std::numeric_limits<unsigned char>::min(), std::numeric_limits<unsigned char>::max());
642 chat_session_packet->SetChatSession(chat_session_data);
643 Send(chat_session_packet);
648#if PROTOCOL_VERSION > 763
651 state = ConnectionState::Play;
652 std::shared_ptr<ServerboundFinishConfigurationPacket> finish_config_packet = std::make_shared<ServerboundFinishConfigurationPacket>();
653 Send(finish_config_packet);
658 std::shared_ptr<ServerboundKeepAliveConfigurationPacket> keep_alive_packet = std::make_shared<ServerboundKeepAliveConfigurationPacket>();
659 keep_alive_packet->SetId_(packet.GetId_());
660 Send(keep_alive_packet);
665 std::shared_ptr<ServerboundPongConfigurationPacket> pong_packet = std::make_shared<ServerboundPongConfigurationPacket>();
666 pong_packet->SetId_(packet.GetId_());
672 state = ConnectionState::Configuration;
673 std::shared_ptr<ServerboundConfigurationAcknowledgedPacket> config_ack_packet = std::make_shared<ServerboundConfigurationAcknowledgedPacket>();
674 Send(config_ack_packet);
684 using count_return =
decltype(std::declval<std::chrono::milliseconds>().count());
685 const count_return time_elapsed_ms = std::max(
static_cast<count_return
>(1), std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() -
chunk_batch_start_time).count());
686 std::shared_ptr<ServerboundChunkBatchReceivedPacket> chunk_per_tick_packet = std::make_shared<ServerboundChunkBatchReceivedPacket>();
688 chunk_per_tick_packet->SetDesiredChunksPerTick(packet.GetBatchSize() * 50.0f / time_elapsed_ms);
689 Send(chunk_per_tick_packet);
693#if PROTOCOL_VERSION > 765
696 std::shared_ptr<ServerboundSelectKnownPacksPacket> select_known_packs = std::make_shared<ServerboundSelectKnownPacksPacket>();
698 select_known_packs->SetKnownPacks({});
700 Send(select_known_packs);
704#if PROTOCOL_VERSION < 764
708 std::shared_ptr<ServerboundClientInformationPacket> settings_packet = std::make_shared<ServerboundClientInformationPacket>();
709 settings_packet->SetLanguage(
"fr_FR");
710 settings_packet->SetViewDistance(10);
712 settings_packet->SetChatColors(
true);
713 settings_packet->SetModelCustomisation(0xFF);
714 settings_packet->SetMainHand(1);
#define LOG_INFO(osstream)
#define LOG_FATAL(osstream)
std::shared_ptr< NetworkManager > network_manager
void AddSeenMessage(const std::vector< unsigned char > &signature)
std::pair< std::vector< std::vector< unsigned char > >, ProtocolCraft::LastSeenMessagesUpdate > GetLastSeenMessagesUpdate()
Get both a vector of previous messages signatures and the LastSeenMessagesUpdate object.
void RegisterThread(const std::string &name)
Register the current thread in the map.
static Logger & GetInstance()
virtual void Handle(ProtocolCraft::ClientboundLoginFinishedPacket &packet) override
std::condition_variable process_condition
void AddHandler(ProtocolCraft::Handler *h)
virtual void Handle(ProtocolCraft::ClientboundLoginCompressionPacket &packet) override
std::thread::id GetProcessingThreadId() const
void SendChatCommand(const std::string &command)
std::thread m_thread_process
void Send(const std::shared_ptr< ProtocolCraft::Packet > packet)
void SendChatMessage(const std::string &message)
void ProcessPacket(const std::vector< unsigned char > &bytes)
LastSeenMessagesTracker chat_context
std::shared_ptr< TCP_Com > com
void OnNewRawData(const std::vector< unsigned char > &bytes)
std::chrono::steady_clock::time_point chunk_batch_start_time
const std::string & GetMyName() const
std::shared_ptr< Authentifier > authentifier
const ProtocolCraft::ConnectionState GetConnectionState() const
std::vector< ProtocolCraft::Handler * > subscribed
std::atomic< int > message_sent_index
ProtocolCraft::ConnectionState state
std::queue< std::vector< unsigned char > > packets_to_process
ProtocolCraft::UUID chat_session_uuid
NetworkManager(const std::string &address, const std::string &login_or_microsoft_cache_key_or_mc_token, const AuthType auth_type, const std::vector< ProtocolCraft::Handler * > &handlers={})
std::vector< unsigned char > RSAToBytes(const std::string &s)
std::vector< unsigned char > DecodeBase64(const std::string &s)
bool WaitForCondition(const std::function< bool()> &condition, const long long int timeout_ms=0, const long long int check_interval_ms=10)
std::vector< unsigned char > Decompress(const std::vector< unsigned char > &compressed, const int start=0)
std::vector< unsigned char > Compress(const std::vector< unsigned char > &raw)
std::shared_ptr< Packet > CreateClientboundPacket(const ConnectionState state, const int id)
std::array< unsigned char, 16 > UUID
std::vector< unsigned char >::const_iterator ReadIterator