/******************************************************************** Minecraft: Pocket Edition - Decompilation Project Copyright (C) 2023 iProgramInCpp The following code is licensed under the BSD 1 clause license. SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ #include "RakNetInstance.hpp" #include "MinecraftPackets.hpp" #include "GetTime.h" //#define LOG_PACKETS #ifdef LOG_PACKETS #define LOG_PACKET(str, ...) LOG_I(str, __VA_ARGS__) #else #define LOG_PACKET(str, ...) #endif RakNetInstance::RakNetInstance() { m_bIsHost = false; m_pRakPeerInterface = RakNet::RakPeerInterface::GetInstance(); m_pRakPeerInterface->SetOccasionalPing(true); } RakNetInstance::~RakNetInstance() { if (m_pRakPeerInterface) { m_pRakPeerInterface->Shutdown(100); RakNet::RakPeerInterface::DestroyInstance(m_pRakPeerInterface); m_pRakPeerInterface = nullptr; } } void RakNetInstance::announceServer(const std::string& name) { if (m_bIsHost && m_pRakPeerInterface->IsActive()) { RakNet::RakString rs; rs += "MCCPP;Demo;"; // @NOTE: still adding 'Demo' despite not actually being a demo. rs += name.c_str(); RakNet::BitStream bs; rs.Serialize(&bs); m_pRakPeerInterface->SetOfflinePingResponse((const char*)bs.GetData(), bs.GetNumberOfBytesUsed()); } } void RakNetInstance::clearServerList() { m_servers.clear(); } bool RakNetInstance::connect(const char* host, int port) { RakNet::SocketDescriptor sd; disconnect(); if (m_pRakPeerInterface->Startup(4, &sd, 1) != RakNet::RAKNET_STARTED) return false; LOG_I("Connecting to %s", host); // Was evaluated to a bool, CONNECTION_ATTEMPT_STARTED is 0 return m_pRakPeerInterface->Connect(host, port, nullptr, 0) != RakNet::CONNECTION_ATTEMPT_STARTED; } void RakNetInstance::disconnect() { if (m_pRakPeerInterface->IsActive()) m_pRakPeerInterface->Shutdown(500); m_bIsHost = false; m_bPingingForHosts = false; } RakNet::RakPeerInterface* RakNetInstance::getPeer() { return m_pRakPeerInterface; } std::vector* RakNetInstance::getServerList() { return &m_servers; } bool RakNetInstance::host(const std::string& name, int port, int maxConnections) { if (m_pRakPeerInterface->IsActive()) m_pRakPeerInterface->Shutdown(500); RakNet::SocketDescriptor sd(port, nullptr); m_pRakPeerInterface->SetMaximumIncomingConnections(maxConnections); int result = m_pRakPeerInterface->Startup(maxConnections, &sd, 1); m_bIsHost = true; m_bPingingForHosts = false; return result == RakNet::RAKNET_STARTED; } bool RakNetInstance::isMyLocalGuid(const RakNet::RakNetGUID& guid) { if (!m_pRakPeerInterface->IsActive()) return false; return m_pRakPeerInterface->GetMyGUID() == guid; } void RakNetInstance::pingForHosts(int port) { if (!m_pRakPeerInterface->IsActive()) { RakNet::SocketDescriptor sd; m_pRakPeerInterface->Startup(4, &sd, 1); } m_hostPingPort = port; m_bPingingForHosts = true; m_startedPingingAt = RakNet::GetTimeMS(); m_pRakPeerInterface->Ping("255.255.255.255", port, true, 0); } void RakNetInstance::runEvents(NetEventCallback* callback) { while (true) { RakNet::Packet* pPacket = m_pRakPeerInterface->Receive(); if (!pPacket) break; uint8_t packetType = *(pPacket->data); RakNet::BitStream* pBitStream = new RakNet::BitStream(pPacket->data + 1, pPacket->length - 1, 0); LOG_PACKET("Recieved packet from %s (id: %d bitStream: 0x%x length: %u)", pPacket->systemAddress.ToString(), packetType, pBitStream, pPacket->length); // @NOTE: why -1? if (packetType >= PACKET_LOGIN - 1) { Packet* pUserPacket = MinecraftPackets::createPacket(packetType); if (pUserPacket) { pUserPacket->read(pBitStream); pUserPacket->handle(pPacket->guid, callback); delete pUserPacket; } else { LOG_W("Received unrecognized packet type: %d", packetType); } } else if (packetType > ID_DETECT_LOST_CONNECTIONS) switch (packetType) { case ID_CONNECTION_REQUEST_ACCEPTED: { // @BUG: Two players sending connection requests at the same time could cause one of them to fail to connect m_guid = pPacket->guid; callback->onConnect(pPacket->guid); break; } case ID_CONNECTION_ATTEMPT_FAILED: { callback->onUnableToConnect(); break; } case ID_NEW_INCOMING_CONNECTION: { callback->onNewClient(pPacket->guid); break; } case ID_DISCONNECTION_NOTIFICATION: case ID_CONNECTION_LOST: { callback->onDisconnect(pPacket->guid); break; } case ID_UNCONNECTED_PONG: { unsigned int thing; RakNet::RakString serverInfo, mccppDemo = "MCCPP;Demo;"; pBitStream->Read(thing); pBitStream->Read(serverInfo); // check if the server info starts with MCCPP;Demo; if (serverInfo.GetLength() < mccppDemo.GetLength()) break; RakNet::RakString headerSubstr = serverInfo.SubStr(0, unsigned(mccppDemo.GetLength())); if (mccppDemo.StrCmp(headerSubstr) != 0) break; // update the info of a pinged compatible server, if possible. for (int i = 0; i < m_servers.size(); i++) { PingedCompatibleServer& server = m_servers.at(i); if (server.m_address == pPacket->systemAddress) { server.m_lastPinged = RakNet::GetTimeMS(); server.m_name = serverInfo.SubStr(unsigned(mccppDemo.GetLength()), unsigned(serverInfo.GetLength() - mccppDemo.GetLength())); goto SKIP_ADDING_NEW_ENTRY; } } // Add a new entry. { PingedCompatibleServer pcs; pcs.m_address = pPacket->systemAddress; pcs.m_lastPinged = RakNet::GetTimeMS(); pcs.m_name = serverInfo.SubStr(unsigned(mccppDemo.GetLength()), unsigned(serverInfo.GetLength() - mccppDemo.GetLength())); m_servers.push_back(pcs); } SKIP_ADDING_NEW_ENTRY: break; } default: LOG_W("Received unrecognized RakNet packet type: %d", packetType); break; } m_pRakPeerInterface->DeallocatePacket(pPacket); if (pBitStream) delete pBitStream; } if (m_bPingingForHosts) { int timeDiff = RakNet::GetTimeMS() - m_startedPingingAt; if (timeDiff > 1000) { for (std::vector::iterator it = m_servers.begin(); it != m_servers.end(); ) { if (RakNet::GetTimeMS() - it->m_lastPinged <= 3000) { it++; continue; } it = m_servers.erase(it); } pingForHosts(m_hostPingPort); } } } // this broadcasts a packet to all other connected peers void RakNetInstance::send(Packet* packet) { RakNet::BitStream bs; packet->write(&bs); uint32_t result; if (m_bIsHost) { result = m_pRakPeerInterface->Send(&bs, HIGH_PRIORITY, RELIABLE, 0, RakNet::UNASSIGNED_RAKNET_GUID, true); } else { // send it to the host instead result = m_pRakPeerInterface->Send(&bs, HIGH_PRIORITY, RELIABLE, 0, m_guid, false); } if (result != 0) { #ifdef LOG_PACKETS uint8_t packetId; bs.Read(packetId); LOG_PACKET("Sent packet (id: %d guid: %s)", packetId, m_bIsHost ? "UNASSIGNED_SYSTEM_ADDRESS" : m_guid.ToString()); #endif } else { LOG_E("Failed to send packet!"); } delete packet; // return 1300; --- ida tells me this returns 1300. Huh } // this sends a specific peer a message void RakNetInstance::send(const RakNet::RakNetGUID& guid, Packet* packet) { RakNet::BitStream bs; packet->write(&bs); m_pRakPeerInterface->Send(&bs, HIGH_PRIORITY, RELIABLE, 0, guid, false); delete packet; // return 1300; --- ida tells me this returns 1300. Huh } void RakNetInstance::stopPingForHosts() { if (!m_bPingingForHosts) return; m_pRakPeerInterface->Shutdown(0); m_bPingingForHosts = false; }