diff --git a/dCommon/Game.h b/dCommon/Game.h index d8113497..a0dfb9f5 100644 --- a/dCommon/Game.h +++ b/dCommon/Game.h @@ -16,6 +16,8 @@ class EntityManager; class dZoneManager; class PlayerContainer; +#include "TransportPeerInterface.h" + namespace Game { using signal_t = volatile std::sig_atomic_t; extern Logger* logger; @@ -24,7 +26,7 @@ namespace Game { extern dChatFilter* chatFilter; extern dConfig* config; extern std::mt19937 randomEngine; - extern RakPeerInterface* chatServer; + extern TransportPeerInterface* chatServer; extern AssetManager* assetManager; extern SystemAddress chatSysAddr; extern signal_t lastSignal; diff --git a/dCommon/dEnums/eMasterMessageType.h b/dCommon/dEnums/eManagerMessageType.h similarity index 75% rename from dCommon/dEnums/eMasterMessageType.h rename to dCommon/dEnums/eManagerMessageType.h index 5c867d70..57a51ed2 100644 --- a/dCommon/dEnums/eMasterMessageType.h +++ b/dCommon/dEnums/eManagerMessageType.h @@ -1,9 +1,9 @@ -#ifndef __EMASTERMESSAGETYPE__H__ -#define __EMASTERMESSAGETYPE__H__ +#ifndef __EMANAGERMESSAGETYPE__H__ +#define __EMANAGERMESSAGETYPE__H__ #include -enum class eMasterMessageType : uint32_t { +enum class eManagerMessageType : uint32_t { REQUEST_PERSISTENT_ID = 1, REQUEST_PERSISTENT_ID_RESPONSE, REQUEST_ZONE_TRANSFER, @@ -33,4 +33,4 @@ enum class eMasterMessageType : uint32_t { NEW_SESSION_ALERT }; -#endif //!__EMASTERMESSAGETYPE__H__ +#endif //!__EMANAGERMESSAGETYPE__H__ diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.cpp b/dGame/dComponents/RocketLaunchpadControlComponent.cpp index c0c90581..cc67332c 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.cpp +++ b/dGame/dComponents/RocketLaunchpadControlComponent.cpp @@ -18,7 +18,7 @@ #include "BitStreamUtils.h" #include "eObjectWorldState.h" #include "eConnectionType.h" -#include "eMasterMessageType.h" +#include "eManagerMessageType.h" RocketLaunchpadControlComponent::RocketLaunchpadControlComponent(Entity* parent, int rocketId) : Component(parent) { auto query = CDClientDatabase::CreatePreppedStmt( @@ -137,7 +137,7 @@ LWOCLONEID RocketLaunchpadControlComponent::GetSelectedCloneId(LWOOBJID player) void RocketLaunchpadControlComponent::TellMasterToPrepZone(int zoneID) { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PREP_ZONE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::PREP_ZONE); bitStream.Write(zoneID); Game::server->SendToMaster(bitStream); } diff --git a/dGame/dUtilities/ObjectIDManager.cpp b/dGame/dUtilities/ObjectIDManager.cpp index a30ede05..5ccb906f 100644 --- a/dGame/dUtilities/ObjectIDManager.cpp +++ b/dGame/dUtilities/ObjectIDManager.cpp @@ -5,6 +5,7 @@ #include "Database.h" #include "Logger.h" #include "Game.h" +#include "dServer.h" //! The persistent ID request struct PersistentIDRequest { @@ -25,7 +26,7 @@ namespace { void ObjectIDManager::RequestPersistentID(const std::function callback) { const auto& request = Requests.emplace_back(++CurrentRequestID, callback); - MasterPackets::SendPersistentIDRequest(Game::server, request.requestID); + MasterPackets::SendPersistentIDRequest(Game::server->GetTransportLayerPtr(), request.requestID); } //! Handles a persistent ID response diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp index 00add608..1120dbf4 100644 --- a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp @@ -45,9 +45,10 @@ // Enums #include "eGameMasterLevel.h" -#include "eMasterMessageType.h" +#include "eManagerMessageType.h" #include "eInventoryType.h" #include "ePlayerFlag.h" +#include namespace DEVGMCommands { @@ -503,7 +504,7 @@ namespace DEVGMCommands { void ShutdownUniverse(Entity* entity, const SystemAddress& sysAddr, const std::string args) { //Tell the master server that we're going to be shutting down whole "universe": CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN_UNIVERSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::SHUTDOWN_UNIVERSE); Game::server->SendToMaster(bitStream); ChatPackets::SendSystemMessage(sysAddr, u"Sent universe shutdown notification to master."); @@ -1247,8 +1248,16 @@ namespace DEVGMCommands { scriptedActivityComponent->ReloadConfig(); } - Game::server->UpdateMaximumMtuSize(); - Game::server->UpdateBandwidthLimit(); + + if (Game::server->GetTransportType() == TransportType::RakNet) { + const auto& transport = Game::server->GetTransportLayer(); + + auto* raknetTransport = static_cast(transport.get()); + + raknetTransport->UpdateMaximumMtuSize(); + raknetTransport->UpdateBandwidthLimit(); + } + ChatPackets::SendSystemMessage(sysAddr, u"Successfully reloaded config for world!"); } diff --git a/dMasterServer/InstanceManager.cpp b/dMasterServer/InstanceManager.cpp index 3ec42634..8e16d8bf 100644 --- a/dMasterServer/InstanceManager.cpp +++ b/dMasterServer/InstanceManager.cpp @@ -10,7 +10,7 @@ #include "MasterPackets.h" #include "BitStreamUtils.h" #include "eConnectionType.h" -#include "eMasterMessageType.h" +#include "eManagerMessageType.h" #include "Start.h" @@ -157,7 +157,7 @@ void InstanceManager::ReadyInstance(Instance* instance) { LOG("Responding to pending request %llu -> %i (%i)", request, zoneId.GetMapID(), zoneId.GetCloneID()); MasterPackets::SendZoneTransferResponse( - Game::server, + Game::server->GetTransportLayerPtr(), request.sysAddr, request.id, request.mythranShift, @@ -177,7 +177,7 @@ void InstanceManager::RequestAffirmation(Instance* instance, const PendingInstan CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::AFFIRM_TRANSFER_REQUEST); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::AFFIRM_TRANSFER_REQUEST); bitStream.Write(request.id); @@ -200,7 +200,7 @@ void InstanceManager::AffirmTransfer(Instance* instance, const uint64_t transfer const auto& zoneId = instance->GetZoneID(); MasterPackets::SendZoneTransferResponse( - Game::server, + Game::server->GetTransportLayerPtr(), request.sysAddr, request.id, request.mythranShift, @@ -359,7 +359,7 @@ bool Instance::GetShutdownComplete() const { void Instance::Shutdown() { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::SHUTDOWN); Game::server->Send(bitStream, this->m_SysAddr, false); diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 05955a85..0fb5d4ad 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -24,7 +24,7 @@ #include "AssetManager.h" #include "BinaryPathFinder.h" #include "eConnectionType.h" -#include "eMasterMessageType.h" +#include "eManagerMessageType.h" //RakNet includes: #include "RakNetDefines.h" @@ -295,7 +295,7 @@ int main(int argc, char** argv) { const auto externalIPString = Game::config->GetValue("external_ip"); if (!externalIPString.empty()) ourIP = externalIPString; - Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal); + Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Manager, Game::config, &Game::lastSignal); std::string master_server_ip = "localhost"; const auto masterServerIPString = Game::config->GetValue("master_ip"); @@ -473,8 +473,8 @@ void HandlePacket(Packet* packet) { if (packet->length < 4) return; if (static_cast(packet->data[1]) == eConnectionType::MASTER) { - switch (static_cast(packet->data[3])) { - case eMasterMessageType::REQUEST_PERSISTENT_ID: { + switch (static_cast(packet->data[3])) { + case eManagerMessageType::REQUEST_PERSISTENT_ID: { LOG("A persistent ID req"); RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -482,11 +482,11 @@ void HandlePacket(Packet* packet) { inStream.Read(requestID); uint32_t objID = PersistentIDManager::GeneratePersistentID(); - MasterPackets::SendPersistentIDResponse(Game::server, packet->systemAddress, requestID, objID); + MasterPackets::SendPersistentIDResponse(Game::server->GetTransportLayerPtr(), packet->systemAddress, requestID, objID); break; } - case eMasterMessageType::REQUEST_ZONE_TRANSFER: { + case eManagerMessageType::REQUEST_ZONE_TRANSFER: { LOG("Received zone transfer req"); RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -522,7 +522,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::SERVER_INFO: { + case eManagerMessageType::SERVER_INFO: { //MasterPackets::HandleServerInfo(packet); //This is here because otherwise we'd have to include IM in @@ -543,6 +543,8 @@ void HandlePacket(Packet* packet) { inStream.Read(theirServerType); inStream.Read(theirIP); + LOG("Received server info, %i %i %i %i %s", theirPort, theirZoneID, theirInstanceID, theirServerType, theirIP.string.c_str()); + if (theirServerType == ServerType::World) { if (!Game::im->IsPortInUse(theirPort)) { Instance* in = new Instance(theirIP.string, theirPort, theirZoneID, theirInstanceID, 0, 12, 12); @@ -551,12 +553,18 @@ void HandlePacket(Packet* packet) { copy.binaryAddress = packet->systemAddress.binaryAddress; copy.port = packet->systemAddress.port; + LOG("Adding instance %i %i on %s:%i (%s:%i)", theirZoneID, theirInstanceID, packet->systemAddress.ToString(false), packet->systemAddress.port, theirIP.string.c_str(), theirPort); + in->SetSysAddr(copy); Game::im->AddInstance(in); } else { - auto instance = Game::im->FindInstance( + auto* instance = Game::im->FindInstance( theirZoneID, static_cast(theirInstanceID)); if (instance) { + LOG("Setting address of instance %i %i from %s:%i to %s:%i (%s:%i)", + theirZoneID, theirInstanceID, instance->GetIP().c_str(), instance->GetPort(), packet->systemAddress.ToString(false), packet->systemAddress.port, theirIP.string.c_str(), theirPort + ); + instance->SetSysAddr(packet->systemAddress); } } @@ -583,7 +591,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::SET_SESSION_KEY: { + case eManagerMessageType::SET_SESSION_KEY: { CINSTREAM_SKIP_HEADER; uint32_t sessionKey = 0; inStream.Read(sessionKey); @@ -595,7 +603,7 @@ void HandlePacket(Packet* packet) { activeSessions.erase(it.first); CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::NEW_SESSION_ALERT); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::NEW_SESSION_ALERT); bitStream.Write(sessionKey); bitStream.Write(username); SEND_PACKET_BROADCAST; @@ -609,7 +617,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::REQUEST_SESSION_KEY: { + case eManagerMessageType::REQUEST_SESSION_KEY: { CINSTREAM_SKIP_HEADER; LUWString username; inStream.Read(username); @@ -617,7 +625,7 @@ void HandlePacket(Packet* packet) { for (auto key : activeSessions) { if (key.second == username.GetAsString()) { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SESSION_KEY_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::SESSION_KEY_RESPONSE); bitStream.Write(key.first); bitStream.Write(username); Game::server->Send(bitStream, packet->systemAddress, false); @@ -627,7 +635,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::PLAYER_ADDED: { + case eManagerMessageType::PLAYER_ADDED: { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -647,7 +655,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::PLAYER_REMOVED: { + case eManagerMessageType::PLAYER_REMOVED: { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -665,7 +673,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::CREATE_PRIVATE_ZONE: { + case eManagerMessageType::CREATE_PRIVATE_ZONE: { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -689,7 +697,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::REQUEST_PRIVATE_ZONE: { + case eManagerMessageType::REQUEST_PRIVATE_ZONE: { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -719,12 +727,12 @@ void HandlePacket(Packet* packet) { const auto& zone = instance->GetZoneID(); - MasterPackets::SendZoneTransferResponse(Game::server, packet->systemAddress, requestID, static_cast(mythranShift), zone.GetMapID(), instance->GetInstanceID(), zone.GetCloneID(), instance->GetIP(), instance->GetPort()); + MasterPackets::SendZoneTransferResponse(Game::server->GetTransportLayerPtr(), packet->systemAddress, requestID, static_cast(mythranShift), zone.GetMapID(), instance->GetInstanceID(), zone.GetCloneID(), instance->GetIP(), instance->GetPort()); break; } - case eMasterMessageType::WORLD_READY: { + case eManagerMessageType::WORLD_READY: { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -748,7 +756,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::PREP_ZONE: { + case eManagerMessageType::PREP_ZONE: { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -764,7 +772,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::AFFIRM_TRANSFER_RESPONSE: { + case eManagerMessageType::AFFIRM_TRANSFER_RESPONSE: { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -784,7 +792,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::SHUTDOWN_RESPONSE: { + case eManagerMessageType::SHUTDOWN_RESPONSE: { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -799,7 +807,7 @@ void HandlePacket(Packet* packet) { break; } - case eMasterMessageType::SHUTDOWN_UNIVERSE: { + case eManagerMessageType::SHUTDOWN_UNIVERSE: { LOG("Received shutdown universe command, shutting down in 10 minutes."); Game::universeShutdownRequested = true; break; @@ -829,7 +837,7 @@ int ShutdownSequence(int32_t signal) { { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::SHUTDOWN); Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); LOG("Triggered master shutdown"); } diff --git a/dNet/AuthPackets.cpp b/dNet/AuthPackets.cpp index 715188e8..3539f76c 100644 --- a/dNet/AuthPackets.cpp +++ b/dNet/AuthPackets.cpp @@ -21,7 +21,7 @@ #include "eLoginResponse.h" #include "eConnectionType.h" #include "eServerMessageType.h" -#include "eMasterMessageType.h" +#include "eManagerMessageType.h" #include "eGameMasterLevel.h" #include "StringifiedEnum.h" namespace { @@ -60,7 +60,7 @@ void AuthPackets::HandleHandshake(dServer* server, Packet* packet) { uint16_t port; inStream.Read(port); - if (port != packet->systemAddress.port) LOG("WARNING: Port written in packet does not match the port the client is connecting over!"); + if (port != packet->systemAddress.port) LOG("WARNING: Port written in packet does not match the port the client is connecting over, %i != %i!", port, packet->systemAddress.port); inStream.IgnoreBytes(33); @@ -297,7 +297,7 @@ void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAdd //Inform the master server that we've created a session for this user: if (responseCode == eLoginResponse::SUCCESS) { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SET_SESSION_KEY); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::SET_SESSION_KEY); bitStream.Write(sessionKey); bitStream.Write(LUString(username)); server->SendToMaster(bitStream); diff --git a/dNet/CMakeLists.txt b/dNet/CMakeLists.txt index 15cdda42..4d068ab8 100644 --- a/dNet/CMakeLists.txt +++ b/dNet/CMakeLists.txt @@ -5,10 +5,33 @@ set(DNET_SOURCES "AuthPackets.cpp" "MasterPackets.cpp" "PacketUtils.cpp" "WorldPackets.cpp" + "RakNetTransportLayer.cpp" + "RakNetPeer.cpp" "ZoneInstanceManager.cpp") +find_package(OpenSSL REQUIRED) + +# Find boost for asio +cmake_policy(SET CMP0167 NEW) +find_package(Boost QUIET COMPONENTS system) + +if(Boost_FOUND) + message(STATUS "Boost found, enabling TCP transport support") + + # Add to source list + list(APPEND DNET_SOURCES "TcpTransportLayer.cpp" "TcpPeer.cpp" "TcpSession.cpp") +endif() + add_library(dNet STATIC ${DNET_SOURCES}) -target_link_libraries(dNet PRIVATE bcrypt MD5) + +if(Boost_FOUND) + target_compile_definitions(dNet PRIVATE NET_ENABLE_TCP_TRANSPORT) + + target_link_libraries(dNet PRIVATE bcrypt MD5 OpenSSL::SSL OpenSSL::Crypto OpenSSL::applink Boost::system) +else() + target_link_libraries(dNet PRIVATE bcrypt MD5) +endif() + target_include_directories(dNet PRIVATE "${PROJECT_SOURCE_DIR}/dCommon" "${PROJECT_SOURCE_DIR}/dCommon/dEnums" diff --git a/dNet/MasterPackets.cpp b/dNet/MasterPackets.cpp index 7bd8f4a5..fc5cccab 100644 --- a/dNet/MasterPackets.cpp +++ b/dNet/MasterPackets.cpp @@ -1,23 +1,23 @@ #include "MasterPackets.h" #include "BitStream.h" #include "dCommonVars.h" -#include "dServer.h" +#include "TransportLayer.h" #include "eConnectionType.h" -#include "eMasterMessageType.h" +#include "eManagerMessageType.h" #include "BitStreamUtils.h" #include -void MasterPackets::SendPersistentIDRequest(dServer* server, uint64_t requestID) { +void MasterPackets::SendPersistentIDRequest(TransportLayer* server, uint64_t requestID) { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_PERSISTENT_ID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::REQUEST_PERSISTENT_ID); bitStream.Write(requestID); - server->SendToMaster(bitStream); + server->SendToManager(bitStream); } -void MasterPackets::SendPersistentIDResponse(dServer* server, const SystemAddress& sysAddr, uint64_t requestID, uint32_t objID) { +void MasterPackets::SendPersistentIDResponse(TransportLayer* server, const SystemAddress& sysAddr, uint64_t requestID, uint32_t objID) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::REQUEST_PERSISTENT_ID_RESPONSE); bitStream.Write(requestID); bitStream.Write(objID); @@ -25,21 +25,21 @@ void MasterPackets::SendPersistentIDResponse(dServer* server, const SystemAddres server->Send(bitStream, sysAddr, false); } -void MasterPackets::SendZoneTransferRequest(dServer* server, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t cloneID) { +void MasterPackets::SendZoneTransferRequest(TransportLayer* server, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t cloneID) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_ZONE_TRANSFER); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::REQUEST_ZONE_TRANSFER); bitStream.Write(requestID); bitStream.Write(mythranShift); bitStream.Write(zoneID); bitStream.Write(cloneID); - server->SendToMaster(bitStream); + server->SendToManager(bitStream); } -void MasterPackets::SendZoneCreatePrivate(dServer* server, uint32_t zoneID, uint32_t cloneID, const std::string& password) { +void MasterPackets::SendZoneCreatePrivate(TransportLayer* server, uint32_t zoneID, uint32_t cloneID, const std::string& password) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::CREATE_PRIVATE_ZONE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::CREATE_PRIVATE_ZONE); bitStream.Write(zoneID); bitStream.Write(cloneID); @@ -49,12 +49,12 @@ void MasterPackets::SendZoneCreatePrivate(dServer* server, uint32_t zoneID, uint bitStream.Write(character); } - server->SendToMaster(bitStream); + server->SendToManager(bitStream); } -void MasterPackets::SendZoneRequestPrivate(dServer* server, uint64_t requestID, bool mythranShift, const std::string& password) { +void MasterPackets::SendZoneRequestPrivate(TransportLayer* server, uint64_t requestID, bool mythranShift, const std::string& password) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_PRIVATE_ZONE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::REQUEST_PRIVATE_ZONE); bitStream.Write(requestID); bitStream.Write(mythranShift); @@ -64,22 +64,22 @@ void MasterPackets::SendZoneRequestPrivate(dServer* server, uint64_t requestID, bitStream.Write(character); } - server->SendToMaster(bitStream); + server->SendToManager(bitStream); } -void MasterPackets::SendWorldReady(dServer* server, LWOMAPID zoneId, LWOINSTANCEID instanceId) { +void MasterPackets::SendWorldReady(TransportLayer* server, LWOMAPID zoneId, LWOINSTANCEID instanceId) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::WORLD_READY); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::WORLD_READY); bitStream.Write(zoneId); bitStream.Write(instanceId); - server->SendToMaster(bitStream); + server->SendToManager(bitStream); } -void MasterPackets::SendZoneTransferResponse(dServer* server, const SystemAddress& sysAddr, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, const std::string& serverIP, uint32_t serverPort) { +void MasterPackets::SendZoneTransferResponse(TransportLayer* server, const SystemAddress& sysAddr, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, const std::string& serverIP, uint32_t serverPort) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_ZONE_TRANSFER_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::REQUEST_ZONE_TRANSFER_RESPONSE); bitStream.Write(requestID); bitStream.Write(mythranShift); @@ -109,9 +109,9 @@ void MasterPackets::HandleServerInfo(Packet* packet) { //TODO: Actually mark this server as an available server in the manager } -void MasterPackets::SendServerInfo(dServer* server, Packet* packet) { +void MasterPackets::SendServerInfo(TransportLayer* server, Packet* packet) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SERVER_INFO); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::SERVER_INFO); bitStream.Write(server->GetPort()); bitStream.Write(server->GetZoneID()); @@ -119,5 +119,5 @@ void MasterPackets::SendServerInfo(dServer* server, Packet* packet) { bitStream.Write(server->GetServerType()); bitStream.Write(LUString(server->GetIP())); - server->SendToMaster(bitStream); + server->SendToManager(bitStream); } diff --git a/dNet/MasterPackets.h b/dNet/MasterPackets.h index 93fd158e..1f1d0de3 100644 --- a/dNet/MasterPackets.h +++ b/dNet/MasterPackets.h @@ -5,23 +5,23 @@ #include #include "RakNetTypes.h" #include "dCommonVars.h" -class dServer; +class TransportLayer; namespace MasterPackets { - void SendPersistentIDRequest(dServer* server, uint64_t requestID); //Called from the World server - void SendPersistentIDResponse(dServer* server, const SystemAddress& sysAddr, uint64_t requestID, uint32_t objID); + void SendPersistentIDRequest(TransportLayer* server, uint64_t requestID); //Called from the World server + void SendPersistentIDResponse(TransportLayer* server, const SystemAddress& sysAddr, uint64_t requestID, uint32_t objID); - void SendZoneTransferRequest(dServer* server, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t cloneID); - void SendZoneTransferResponse(dServer* server, const SystemAddress& sysAddr, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, const std::string& serverIP, uint32_t serverPort); + void SendZoneTransferRequest(TransportLayer* server, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t cloneID); + void SendZoneTransferResponse(TransportLayer* server, const SystemAddress& sysAddr, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, const std::string& serverIP, uint32_t serverPort); void HandleServerInfo(Packet* packet); - void SendServerInfo(dServer* server, Packet* packet); + void SendServerInfo(TransportLayer* server, Packet* packet); - void SendZoneCreatePrivate(dServer* server, uint32_t zoneID, uint32_t cloneID, const std::string& password); + void SendZoneCreatePrivate(TransportLayer* server, uint32_t zoneID, uint32_t cloneID, const std::string& password); - void SendZoneRequestPrivate(dServer* server, uint64_t requestID, bool mythranShift, const std::string& password); + void SendZoneRequestPrivate(TransportLayer* server, uint64_t requestID, bool mythranShift, const std::string& password); - void SendWorldReady(dServer* server, LWOMAPID zoneId, LWOINSTANCEID instanceId); + void SendWorldReady(TransportLayer* server, LWOMAPID zoneId, LWOINSTANCEID instanceId); void HandleSetSessionKey(Packet* packet); } diff --git a/dNet/RakNetPeer.cpp b/dNet/RakNetPeer.cpp new file mode 100644 index 00000000..a85de751 --- /dev/null +++ b/dNet/RakNetPeer.cpp @@ -0,0 +1,48 @@ +#include "RakNetPeer.h" + +#include "RakPeerInterface.h" + +RakNetPeer::RakNetPeer(RakPeerInterface* peer, const std::string& ip, int port, const std::string& password) +{ + m_Peer = peer; + m_IP = ip; + m_Port = port; + m_Password = password; +} + +RakNetPeer::~RakNetPeer() +{ + delete m_Peer; +} + +void RakNetPeer::Send( + const RakNet::BitStream* bitStream, + PacketPriority priority, + PacketReliability reliability, + char orderingChannel, + SystemAddress systemAddress, + bool broadcast +) +{ + m_Peer->Send(bitStream, priority, reliability, orderingChannel, systemAddress, broadcast); +} + +void RakNetPeer::Disconnect() +{ + m_Peer->Shutdown(0); +} + +void RakNetPeer::Reconnect() +{ + m_Peer->Connect(m_IP.c_str(), m_Port, m_Password.c_str(), m_Password.length()); +} + +Packet* RakNetPeer::Receive() +{ + return m_Peer->Receive(); +} + +void RakNetPeer::DeallocatePacket(Packet* packet) +{ + m_Peer->DeallocatePacket(packet); +} diff --git a/dNet/RakNetPeer.h b/dNet/RakNetPeer.h new file mode 100644 index 00000000..0f696f17 --- /dev/null +++ b/dNet/RakNetPeer.h @@ -0,0 +1,32 @@ +#pragma once + +#include "TransportPeerInterface.h" + +class RakNetPeer : public TransportPeerInterface { +public: + RakNetPeer(RakPeerInterface* peer, const std::string& ip, int port, const std::string& password); + ~RakNetPeer(); + + void Send( + const RakNet::BitStream* bitStream, + PacketPriority priority, + PacketReliability reliability, + char orderingChannel, + SystemAddress systemAddress, + bool broadcast + ) override; + + void Disconnect() override; + + void Reconnect() override; + + Packet* Receive() override; + + void DeallocatePacket(Packet* packet) override; + +private: + RakPeerInterface* m_Peer; + std::string m_IP; + int m_Port; + std::string m_Password; +}; \ No newline at end of file diff --git a/dNet/RakNetTransportLayer.cpp b/dNet/RakNetTransportLayer.cpp new file mode 100644 index 00000000..5dfbfc0c --- /dev/null +++ b/dNet/RakNetTransportLayer.cpp @@ -0,0 +1,272 @@ +#define _VARIADIC_MAX 10 +#include "RakNetTransportLayer.h" +#include "dNetCommon.h" +#include "Logger.h" +#include "dConfig.h" + +#include "RakNetworkFactory.h" +#include "MessageIdentifiers.h" +#include "eConnectionType.h" +#include "eServerMessageType.h" +#include "eManagerMessageType.h" + +#include "BitStreamUtils.h" +#include "MasterPackets.h" +#include "ZoneInstanceManager.h" +#include "StringifiedEnum.h" +#include "RakNetPeer.h" + +//! Replica Constructor class +class ReplicaConstructor : public ReceiveConstructionInterface { +public: + ReplicaReturnResult ReceiveConstruction(RakNet::BitStream* inBitStream, RakNetTime timestamp, NetworkID networkID, NetworkIDObject* existingObject, SystemAddress senderId, ReplicaManager* caller) { + return REPLICA_PROCESSING_DONE; + } +} ConstructionCB; + +//! Replica Download Sender class +class ReplicaSender : public SendDownloadCompleteInterface { +public: + ReplicaReturnResult SendDownloadComplete(RakNet::BitStream* outBitStream, RakNetTime currentTime, SystemAddress senderId, ReplicaManager* caller) { + return REPLICA_PROCESSING_DONE; + } +} SendDownloadCompleteCB; + +//! Replica Download Receiver class +class ReplicaReceiever : public ReceiveDownloadCompleteInterface { +public: + ReplicaReturnResult ReceiveDownloadComplete(RakNet::BitStream* inBitStream, SystemAddress senderId, ReplicaManager* caller) { + return REPLICA_PROCESSING_DONE; + } +} ReceiveDownloadCompleteCB; + +RakNetTransportLayer::RakNetTransportLayer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string ManagerIP, int ManagerPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) { + mIP = ip; + mPort = port; + mZoneID = zoneID; + mInstanceID = instanceID; + mMaxConnections = maxConnections; + mIsInternal = isInternal; + mUseEncryption = useEncryption; + mLogger = logger; + mManagerIP = ManagerIP; + mManagerPort = ManagerPort; + mManagerConnectionActive = false; + mNetIDManager = nullptr; + mReplicaManager = nullptr; + mServerType = serverType; + mConfig = config; + mShouldShutdown = lastSignal; + mIsOkay = false; +} + +RakNetTransportLayer::~RakNetTransportLayer() { + Shutdown(); +} + +Packet* RakNetTransportLayer::ReceiveFromManager() { + if (!mManagerPeer) return nullptr; + if (!mManagerConnectionActive) ConnectToManager(); + + Packet* packet = mManagerPeer->Receive(); + if (packet) { + if (packet->length < 1) { mManagerPeer->DeallocatePacket(packet); return nullptr; } + + if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { + LOG("Lost our connection to Manager, shutting DOWN!"); + mManagerConnectionActive = false; + //ConnectToManager(); //We'll just shut down now + } + + if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) { + LOG("Established connection to Manager, zone (%i), instance (%i)", this->GetZoneID(), this->GetInstanceID()); + mManagerConnectionActive = true; + mManagerSystemAddress = packet->systemAddress; + MasterPackets::SendServerInfo(this, packet); + } + + if (packet->data[0] == ID_USER_PACKET_ENUM) { + if (static_cast(packet->data[1]) == eConnectionType::MASTER) { + switch (static_cast(packet->data[3])) { + case eManagerMessageType::REQUEST_ZONE_TRANSFER_RESPONSE: { + ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(packet); + break; + } + case eManagerMessageType::SHUTDOWN: + *mShouldShutdown = -2; + break; + + //When we handle these packets in World instead RakNetTransportLayer, we just return the packet's pointer. + default: + + return packet; + } + } + } + + mManagerPeer->DeallocatePacket(packet); + } + + return nullptr; +} + +Packet* RakNetTransportLayer::Receive() { + return mPeer->Receive(); +} + +void RakNetTransportLayer::DeallocatePacket(Packet* packet) { + mPeer->DeallocatePacket(packet); +} + +void RakNetTransportLayer::DeallocateManagerPacket(Packet* packet) { + mManagerPeer->DeallocatePacket(packet); +} + +void RakNetTransportLayer::Send(RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast) { + mPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, sysAddr, broadcast); +} + +void RakNetTransportLayer::SendToManager(RakNet::BitStream& bitStream) { + if (!mManagerConnectionActive) ConnectToManager(); + mManagerPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, mManagerSystemAddress, false); +} + +void RakNetTransportLayer::Disconnect(const SystemAddress& sysAddr, eServerDisconnectIdentifiers disconNotifyID) { + RakNet::BitStream bitStream; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::DISCONNECT_NOTIFY); + bitStream.Write(disconNotifyID); + mPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, sysAddr, false); + + mPeer->CloseConnection(sysAddr, true); +} + +bool RakNetTransportLayer::IsConnected(const SystemAddress& sysAddr) { + return mPeer->IsConnected(sysAddr); +} + +bool RakNetTransportLayer::Startup() { + mIsOkay = StartPeer(); + + //Forcibly log to both the console and our file what ip, port and possibly zoneID / instanceID we're running on: + bool prevLogSetting = mLogger->GetLogToConsole(); + mLogger->SetLogToConsole(true); + + if (mIsOkay) { + if (mZoneID == 0) + LOG("%s Server is listening on %s:%i with encryption: %i", StringifiedEnum::ToString(mServerType).data(), mIP.c_str(), mPort, int(mUseEncryption)); + else + LOG("%s Server is listening on %s:%i with encryption: %i, running zone %i / %i", StringifiedEnum::ToString(mServerType).data(), mIP.c_str(), mPort, int(mUseEncryption), mZoneID, mInstanceID); + } else { LOG("FAILED TO START SERVER ON IP/PORT: %s:%i", mIP.c_str(), mPort); return false; } + + mLogger->SetLogToConsole(prevLogSetting); + + //Connect to Manager if we are not Manager: + if (mServerType != ServerType::Manager) { + SetupForManagerConnection(); + if (!ConnectToManager()) { + LOG("Failed ConnectToManager!"); + } + } + + //Set up Replica if we're a world server: + if (mServerType == ServerType::World) { + /* + mNetIDManager = new NetworkIDManager(); + mNetIDManager->SetIsNetworkIDAuthority(true); + + mReplicaManager = new ReplicaManager(); + mReplicaManager->SetAutoParticipateNewConnections(false); + mReplicaManager->SetAutoConstructToNewParticipants(false); + mReplicaManager->SetAutoSerializeInScope(true); + mReplicaManager->SetReceiveConstructionCB(&ConstructionCB); + mReplicaManager->SetDownloadCompleteCB(&SendDownloadCompleteCB, &ReceiveDownloadCompleteCB); + + mPeer->AttachPlugin(mReplicaManager); + mPeer->SetNetworkIDManager(mNetIDManager); + */ + } + + return mIsOkay; +} + +void RakNetTransportLayer::UpdateMaximumMtuSize() { + auto maxMtuSize = mConfig->GetValue("maximum_mtu_size"); + mPeer->SetMTUSize(maxMtuSize.empty() ? 1228 : std::stoi(maxMtuSize)); +} + +void RakNetTransportLayer::UpdateBandwidthLimit() { + auto newBandwidth = mConfig->GetValue("maximum_outgoing_bandwidth"); + mPeer->SetPerConnectionOutgoingBandwidthLimit(!newBandwidth.empty() ? std::stoi(newBandwidth) : 0); +} + +void RakNetTransportLayer::Shutdown() { + if (mPeer) { + mPeer->Shutdown(1000); + RakNetworkFactory::DestroyRakPeerInterface(mPeer); + } + + if (mNetIDManager) { + delete mNetIDManager; + mNetIDManager = nullptr; + } + + if (mReplicaManager) { + delete mReplicaManager; + mReplicaManager = nullptr; + } + + if (mServerType != ServerType::Manager && mManagerPeer) { + mManagerPeer->Shutdown(1000); + RakNetworkFactory::DestroyRakPeerInterface(mManagerPeer); + } +} + +bool RakNetTransportLayer::StartPeer() { + mSocketDescriptor = SocketDescriptor(uint16_t(mPort), 0); + mPeer = RakNetworkFactory::GetRakPeerInterface(); + + if (!mPeer) return false; + if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false; + + if (mIsInternal) { + mPeer->SetIncomingPassword("3.25 DARKFLAME1", 15); + } else { + UpdateBandwidthLimit(); + UpdateMaximumMtuSize(); + mPeer->SetIncomingPassword("3.25 ND1", 8); + } + + mPeer->SetMaximumIncomingConnections(mMaxConnections); + if (mUseEncryption) mPeer->InitializeSecurity(NULL, NULL, NULL, NULL); + + return true; +} + +void RakNetTransportLayer::SetupForManagerConnection() { + mManagerSocketDescriptor = SocketDescriptor(uint16_t(mPort + 1), 0); + mManagerPeer = RakNetworkFactory::GetRakPeerInterface(); + bool ret = mManagerPeer->Startup(1, 30, &mManagerSocketDescriptor, 1); + if (!ret) LOG("Failed ManagerPeer Startup!"); +} + +bool RakNetTransportLayer::ConnectToManager() { + //LOG("Connection to Manager %s:%d", mManagerIP.c_str(), mManagerPort); + return mManagerPeer->Connect(mManagerIP.c_str(), mManagerPort, "3.25 DARKFLAME1", 15); +} + +int RakNetTransportLayer::GetPing(const SystemAddress& sysAddr) const { + return mPeer->GetAveragePing(sysAddr); +} + +int RakNetTransportLayer::GetLatestPing(const SystemAddress& sysAddr) const { + return mPeer->GetLastPing(sysAddr); +} + +TransportPeerInterface* RakNetTransportLayer::CreateOutgoingTransport(uint32_t peerPort, const std::string& ip, uint32_t port, const std::string& password) const { + auto sock = SocketDescriptor(static_cast(peerPort), 0); + auto* peer = RakNetworkFactory::GetRakPeerInterface(); + peer->Startup(1, 30, &sock, 1); + peer->Connect(ip.c_str(), port, "3.25 ND1", 8); + + return new RakNetPeer(peer, ip, port, password); +} \ No newline at end of file diff --git a/dNet/RakNetTransportLayer.h b/dNet/RakNetTransportLayer.h new file mode 100644 index 00000000..54cbcc03 --- /dev/null +++ b/dNet/RakNetTransportLayer.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include "RakPeerInterface.h" +#include "ReplicaManager.h" +#include "NetworkIDManager.h" + +#include "TransportLayer.h" + +class RakNetTransportLayer : public TransportLayer { +public: + RakNetTransportLayer( + const std::string& ip, + int port, + int instanceID, + int maxConnections, + bool isInternal, + bool useEncryption, + Logger* logger, + const std::string managerIP, + int managerPort, + ServerType serverType, + dConfig* config, + Game::signal_t* shouldShutdown, + unsigned int zoneID = 0 + ); + + ~RakNetTransportLayer(); + + Packet* ReceiveFromManager() override; + Packet* Receive() override; + void DeallocatePacket(Packet* packet) override; + void DeallocateManagerPacket(Packet* packet) override; + void Send(RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast) override; + void SendToManager(RakNet::BitStream& bitStream) override; + + void Disconnect(const SystemAddress& sysAddr, eServerDisconnectIdentifiers disconNotifyID) override; + + bool IsConnected(const SystemAddress& sysAddr) override; + const std::string& GetIP() const override { return mIP; } + const int GetPort() const override { return mPort; } + const int GetMaxConnections() const override { return mMaxConnections; } + const bool GetIsEncrypted() const override { return mUseEncryption; } + const bool GetIsInternal() const override { return mIsInternal; } + const bool GetIsOkay() const override { return mIsOkay; } + Logger* GetLogger() const override { return mLogger; } + const bool GetIsConnectedToManager() const override { return mManagerConnectionActive; } + const unsigned int GetZoneID() const override { return mZoneID; } + const int GetInstanceID() const override { return mInstanceID; } + int GetPing(const SystemAddress& sysAddr) const override; + int GetLatestPing(const SystemAddress& sysAddr) const override; + + const ServerType GetServerType() const override { return mServerType; } + + void UpdateBandwidthLimit(); + void UpdateMaximumMtuSize(); + + TransportPeerInterface* CreateOutgoingTransport( + uint32_t peerPort, + const std::string& ip, + uint32_t port, + const std::string& password + ) const override; + + bool Startup() override; + void Shutdown() override; + +private: + bool StartPeer(); + + void SetupForManagerConnection(); + bool ConnectToManager(); + +private: + Logger* mLogger = nullptr; + dConfig* mConfig = nullptr; + RakPeerInterface* mPeer = nullptr; + ReplicaManager* mReplicaManager = nullptr; + NetworkIDManager* mNetIDManager = nullptr; + + /** + * Whether or not to shut down the server. Pointer to Game::lastSignal. + */ + Game::signal_t* mShouldShutdown = nullptr; + SocketDescriptor mSocketDescriptor; + std::string mIP; + int mPort; + int mMaxConnections; + unsigned int mZoneID; + int mInstanceID; + bool mUseEncryption; + bool mIsInternal; + bool mIsOkay; + bool mManagerConnectionActive; + ServerType mServerType; + + RakPeerInterface* mManagerPeer = nullptr; + SocketDescriptor mManagerSocketDescriptor; + SystemAddress mManagerSystemAddress; + std::string mManagerIP; + int mManagerPort; +}; diff --git a/dNet/TcpPeer.cpp b/dNet/TcpPeer.cpp new file mode 100644 index 00000000..885c462d --- /dev/null +++ b/dNet/TcpPeer.cpp @@ -0,0 +1,269 @@ +#include "TcpPeer.h" + +#include +#include +#include +#include +#include + +#include "Logger.h" +#include "Game.h" + + +TcpPeer::TcpPeer(const std::string& ip, int port, const std::string& password, bool useTls, bool verifyCertificate) + : m_IP(ip), m_Port(port), m_Password(password), m_UseTls(useTls), m_VerifyCertificate(verifyCertificate), + m_Resolver(m_IOService), m_Work(m_IOService), m_IsConnected(false) +{ + if (m_IP == "localhost") { + m_IP = "127.0.0.1"; + } + + // Start the I/O thread for asynchronous operations + m_IOThread = std::thread([this]() { + m_IOService.run(); + }); + + Reconnect(); +} + +TcpPeer::~TcpPeer() { + Disconnect(); + + // Ensure the I/O thread joins + if (m_IOThread.joinable()) { + m_IOThread.join(); + } +} + +void TcpPeer::Reconnect() { + Disconnect(); + + std::scoped_lock lock(m_ConnectMutex); + + boost::asio::ip::tcp::resolver::query query(m_IP, std::to_string(m_Port)); + auto endpointIterator = m_Resolver.resolve(query); + + if (!endpointIterator->endpoint().address().is_v4()) { + LOG("Failed to resolve IP address!"); + return; + } + + // Print out the resolved IP and port + LOG("Resolved IP: %s, Port: %d", endpointIterator->endpoint().address().to_v4().to_string().c_str(), endpointIterator->endpoint().port()); + + // Just use a random 32-bit integer for the system address + m_PeerSystemAddress.binaryAddress = endpointIterator->endpoint().address().to_v4().to_uint(); + m_PeerSystemAddress.port = endpointIterator->endpoint().port(); + + if (m_UseTls) { + m_SSLContext = std::make_unique(boost::asio::ssl::context::sslv23); + m_SSLContext->set_verify_mode(m_VerifyCertificate ? boost::asio::ssl::verify_peer : boost::asio::ssl::verify_none); + m_SSLStream = std::make_unique>(m_IOService, *m_SSLContext); + m_SSLStream->set_verify_callback(boost::asio::ssl::rfc2818_verification(m_IP)); + + boost::asio::async_connect(m_SSLStream->lowest_layer(), endpointIterator, + [this, &lock](const boost::system::error_code& ec, auto) { HandleConnect(ec); }); + } else { + m_Socket = std::make_unique(m_IOService); + boost::asio::async_connect(*m_Socket, endpointIterator, + [this, &lock](const boost::system::error_code& ec, auto) { HandleConnect(ec); }); + } +} + +void TcpPeer::HandleConnect(const boost::system::error_code& ec) { + if (!ec) { + if (m_UseTls) { + m_SSLStream->async_handshake(boost::asio::ssl::stream_base::client, + [this](const boost::system::error_code& handshakeError) { + if (!handshakeError) { + SendConnectionRequest(); + StartAsyncRead(); + } + else { + LOG("Handshake error: %s", handshakeError.message().c_str()); + } + }); + } else { + SendConnectionRequest(); + StartAsyncRead(); + } + } + else { + m_IsConnected = false; + + m_Condition.notify_all(); + + LOG("Connect error: %s", ec.message().c_str()); + } +} + +void TcpPeer::SendConnectionRequest() { + RakNet::BitStream bitStream; + bitStream.Write(ID_CONNECTION_REQUEST); + for (const auto& c : m_Password) { + bitStream.Write(c); + } + + Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_PeerSystemAddress, false); +} + +void TcpPeer::OnReceive(Packet* packet) { + if (packet->length == 0) { + delete[] packet->data; + delete packet; + return; + } + + if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { + Disconnect(); + } + else if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) { + m_IsConnected = true; + m_Condition.notify_all(); + } + + { + std::scoped_lock lock(m_Mutex); + + m_ReceiveBuffer.push(packet); + } +} + +bool TcpPeer::IsConnected() { + std::scoped_lock lock(m_ConnectMutex); + + return m_IsConnected; +} + +bool TcpPeer::WaitForConnection() { + std::unique_lock lock(m_ConnectMutex); + + if (m_IsConnected) { + LOG("Already connected to the server!"); + return true; + } + + if (!m_Condition.wait_for(lock, std::chrono::seconds(5), [this]() { return m_IsConnected; })) { + LOG("Failed to connect to the server!"); + return false; + } + + return m_IsConnected; +} + +void TcpPeer::StartAsyncRead() { + m_HeaderBuffer.resize(sizeof(uint32_t)); + + auto readHeaderCallback = [this](const boost::system::error_code& ec, std::size_t bytesTransferred) { + OnReadHeader(ec, bytesTransferred); + }; + + if (m_UseTls) { + boost::asio::async_read(*m_SSLStream, boost::asio::buffer(m_HeaderBuffer), readHeaderCallback); + } else { + boost::asio::async_read(*m_Socket, boost::asio::buffer(m_HeaderBuffer), readHeaderCallback); + } +} + +void TcpPeer::OnReadHeader(const boost::system::error_code& ec, std::size_t bytesTransferred) { + if (!ec) { + uint32_t size = 0; + std::memcpy(&size, m_HeaderBuffer.data(), sizeof(size)); + + m_BodyBuffer.resize(size); + + auto readBodyCallback = [this](const boost::system::error_code& ec, std::size_t bytesTransferred) { + OnReadBody(ec, bytesTransferred); + }; + + if (m_UseTls) { + boost::asio::async_read(*m_SSLStream, boost::asio::buffer(m_BodyBuffer), readBodyCallback); + } else { + boost::asio::async_read(*m_Socket, boost::asio::buffer(m_BodyBuffer), readBodyCallback); + } + } else { + LOG("Read header error: %s", ec.message().c_str()); + + m_IsConnected = false; + } +} + +void TcpPeer::OnReadBody(const boost::system::error_code& ec, std::size_t bytesTransferred) { + if (!ec) { + auto* packet = new Packet(); + packet->data = new uint8_t[m_BodyBuffer.size()]; + std::memcpy(packet->data, m_BodyBuffer.data(), m_BodyBuffer.size()); + packet->length = m_BodyBuffer.size(); + packet->bitSize = packet->length * 8; + packet->systemAddress = m_PeerSystemAddress; + + OnReceive(packet); + + // Start reading the next packet + StartAsyncRead(); + } else { + LOG("Read body error: %s", ec.message().c_str()); + + m_IsConnected = false; + } +} + +void TcpPeer::Send( + const RakNet::BitStream* bitStream, + PacketPriority priority, + PacketReliability reliability, + char orderingChannel, + SystemAddress systemAddress, + bool broadcast +) { + auto* data = bitStream->GetData(); + uint32_t size = bitStream->GetNumberOfBytesUsed(); + std::vector sendBuffer(sizeof(uint32_t) + size); + + std::memcpy(sendBuffer.data(), &size, sizeof(size)); + std::memcpy(sendBuffer.data() + sizeof(uint32_t), data, size); + + auto sendCallback = [](const boost::system::error_code& ec, std::size_t /*bytesTransferred*/) { + if (ec) { + LOG("Send error: %s", ec.message().c_str()); + } + }; + + if (m_UseTls) { + boost::asio::async_write(*m_SSLStream, boost::asio::buffer(sendBuffer), sendCallback); + } else { + boost::asio::async_write(*m_Socket, boost::asio::buffer(sendBuffer), sendCallback); + } +} + +Packet* TcpPeer::Receive() { + std::unique_lock lock(m_Mutex); + + if (m_ReceiveBuffer.empty()) { + return nullptr; + } + + auto* packet = m_ReceiveBuffer.front(); + m_ReceiveBuffer.pop(); + + return packet; +} + +void TcpPeer::Disconnect() { + std::scoped_lock lock(m_Mutex); + + if (m_IsConnected) { + if (m_UseTls) { + m_SSLStream->lowest_layer().close(); + } else { + m_Socket->close(); + } + + m_IsConnected = false; + } +} + +void TcpPeer::DeallocatePacket(Packet* packet) { + delete[] packet->data; + delete packet; +} \ No newline at end of file diff --git a/dNet/TcpPeer.h b/dNet/TcpPeer.h new file mode 100644 index 00000000..7d08dba4 --- /dev/null +++ b/dNet/TcpPeer.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TransportPeerInterface.h" + +class TcpPeer : public TransportPeerInterface { +public: + TcpPeer(const std::string& ip, int port, const std::string& password, bool useTls, bool verifyCertificate); + ~TcpPeer(); + + void Send( + const RakNet::BitStream* bitStream, + PacketPriority priority, + PacketReliability reliability, + char orderingChannel, + SystemAddress systemAddress, + bool broadcast + ) override; + + void Disconnect() override; + void Reconnect() override; + + Packet* Receive() override; + void DeallocatePacket(Packet* packet) override; + + bool IsConnected(); + bool WaitForConnection(); + +private: + void StartAsyncRead(); + void OnReadHeader(const boost::system::error_code& ec, std::size_t bytesTransferred); + void OnReadBody(const boost::system::error_code& ec, std::size_t bytesTransferred); + void HandleConnect(const boost::system::error_code& ec); + + void SendConnectionRequest(); + + void OnReceive(Packet* packet); + + boost::asio::io_service m_IOService; + boost::asio::ip::tcp::resolver m_Resolver; + std::unique_ptr m_Socket; + std::unique_ptr m_SSLContext; + std::unique_ptr> m_SSLStream; + + + std::string m_IP; + int m_Port; + std::string m_Password; + bool m_UseTls; + bool m_VerifyCertificate; + SystemAddress m_PeerSystemAddress; + bool m_IsConnected; + + std::mutex m_ConnectMutex; + std::mutex m_Mutex; + std::condition_variable m_Condition; + + std::queue m_ReceiveBuffer; + + std::vector m_HeaderBuffer; + std::vector m_BodyBuffer; + + std::thread m_IOThread; + boost::asio::io_service::work m_Work; +}; diff --git a/dNet/TcpSession.cpp b/dNet/TcpSession.cpp new file mode 100644 index 00000000..c1e4edc8 --- /dev/null +++ b/dNet/TcpSession.cpp @@ -0,0 +1,307 @@ +#include "TcpSession.h" + +#include "TcpTransportLayer.h" +#include "Logger.h" +#include "Game.h" +#include "MessageIdentifiers.h" + +#include + +TcpSession::TcpSession(boost::asio::io_service& io_service, boost::asio::ssl::context* sslContext, TcpTransportLayer* server): + m_PingTimerObject(io_service) { + m_Server = server; + + m_UseTls = server->GetIsEncrypted(); + + if (m_UseTls) { + m_SSLStream = std::make_unique>(io_service, *sslContext); + } + else { + m_Socket = std::make_unique(io_service); + } + + m_IsConnected = false; + m_ReadyForUserPackets = false; + m_PingCount = 0; + m_CumulativePing = 0; + m_LastPing = 0; + m_PingTimer = 0; +} + +void TcpSession::Start() { + if (m_UseTls) { + m_SSLStream->async_handshake(boost::asio::ssl::stream_base::server, + [this](const boost::system::error_code& ec) { + if (!ec) { + m_SystemAddress.port = m_SSLStream->lowest_layer().remote_endpoint().port(); + if (m_SSLStream->lowest_layer().remote_endpoint().address().is_v4()) { + m_SystemAddress.binaryAddress = m_SSLStream->lowest_layer().remote_endpoint().address().to_v4().to_uint(); + } else { + m_SystemAddress.binaryAddress = 0; + } + + m_IsConnected = true; + + StartAsyncRead(); + } + }); + } else { + m_SystemAddress.port = m_Socket->remote_endpoint().port(); + if (m_Socket->remote_endpoint().address().is_v4()) { + uint32_t address = m_Socket->remote_endpoint().address().to_v4().to_uint(); + m_SystemAddress.binaryAddress = boost::endian::endian_reverse(address); + } else { + m_SystemAddress.binaryAddress = 0; + } + + m_IsConnected = true; + + StartAsyncRead(); + } + + // Start the ping timer in 5 seconds using boost + m_PingTimerObject.expires_after(std::chrono::seconds(5)); + m_PingTimerObject.async_wait([this](const boost::system::error_code& ec) { + if (!ec) { + DoPing(); + } + }); +} + +void TcpSession::Send(const std::vector& data) { + auto self(shared_from_this()); + auto writeCallback = [this, self](const boost::system::error_code& ec, std::size_t /*bytesTransferred*/) { + OnWrite(ec, 0); // Handle the write result + }; + + uint32_t size = data.size(); + + if (m_UseTls) { + // Write the size of the message + boost::asio::async_write(*m_SSLStream, boost::asio::buffer(&size, sizeof(size)), writeCallback); + boost::asio::async_write(*m_SSLStream, boost::asio::buffer(data), writeCallback); + } else { + // Write the size of the message + boost::asio::async_write(*m_Socket, boost::asio::buffer(&size, sizeof(size)), writeCallback); + boost::asio::async_write(*m_Socket, boost::asio::buffer(data), writeCallback); + } +} + +void TcpSession::Send(const RakNet::BitStream& data) { + auto self(shared_from_this()); + auto writeCallback = [this, self](const boost::system::error_code& ec, std::size_t /*bytesTransferred*/) { + OnWrite(ec, 0); // Handle the write result + }; + + uint32_t size = data.GetNumberOfBytesUsed(); + + if (m_UseTls) { + // Write the size of the message + boost::asio::async_write(*m_SSLStream, boost::asio::buffer(&size, sizeof(size)), writeCallback); + boost::asio::async_write(*m_SSLStream, boost::asio::buffer(data.GetData(), size), writeCallback); + } else { + // Write the size of the message + boost::asio::async_write(*m_Socket, boost::asio::buffer(&size, sizeof(size)), writeCallback); + boost::asio::async_write(*m_Socket, boost::asio::buffer(data.GetData(), size), writeCallback); + } +} + +boost::asio::basic_socket& TcpSession::GetSocket() { + if (m_UseTls) { + return m_SSLStream->lowest_layer(); + } else { + return *m_Socket; + } +} + +void TcpSession::StartAsyncRead() { + if (!m_IsConnected) { + return; + } + + m_HeaderBuffer.resize(sizeof(uint32_t)); + + auto readHeaderCallback = [this](const boost::system::error_code& ec, std::size_t bytesTransferred) { + OnReadHeader(ec, bytesTransferred); + }; + + try { + if (m_UseTls) { + boost::asio::async_read(*m_SSLStream, boost::asio::buffer(m_HeaderBuffer), readHeaderCallback); + } else { + boost::asio::async_read(*m_Socket, boost::asio::buffer(m_HeaderBuffer), readHeaderCallback); + } + } catch (const std::exception& e) { + LOG("Exception: %s", e.what()); + Close(); + } +} + +void TcpSession::OnReadHeader(const boost::system::error_code& ec, std::size_t bytesTransferred) { + if (!ec) { + uint32_t size = 0; + std::memcpy(&size, m_HeaderBuffer.data(), sizeof(size)); + + m_BodyBuffer.resize(size); + + auto readBodyCallback = [this](const boost::system::error_code& ec, std::size_t bytesTransferred) { + OnReadBody(ec, bytesTransferred); + }; + + if (m_UseTls) { + boost::asio::async_read(*m_SSLStream, boost::asio::buffer(m_BodyBuffer), readBodyCallback); + } else { + boost::asio::async_read(*m_Socket, boost::asio::buffer(m_BodyBuffer), readBodyCallback); + } + } else { + LOG("Read header error: %s", ec.message().c_str()); + Close(); + } +} + +void TcpSession::OnReadBody(const boost::system::error_code& ec, std::size_t bytesTransferred) { + if (!ec) { + auto* packet = new Packet(); + packet->data = new uint8_t[m_BodyBuffer.size()]; + std::memcpy(packet->data, m_BodyBuffer.data(), m_BodyBuffer.size()); + packet->length = m_BodyBuffer.size(); + packet->bitSize = packet->length * 8; + packet->systemAddress = m_SystemAddress; + + if (packet->data[0] == ID_USER_PACKET_ENUM && !m_ReadyForUserPackets) { + LOG("Received early user packet from %s:%d", m_SystemAddress.ToString(false), m_SystemAddress.port); + m_UserPacketQueue.push(packet); + } + else if (packet->data[0] == ID_INTERNAL_PING) { + HandlePing(packet); + } + else if (packet->data[0] == ID_CONNECTED_PONG) { + HandlePong(packet); + } + else { + m_Server->OnReceive(packet); + } + + StartAsyncRead(); + } else { + LOG("Read body error: %s", ec.message().c_str()); + Close(); + } +} + +void TcpSession::OnWrite(const boost::system::error_code& ec, std::size_t bytesTransferred) { + if (ec) { + LOG("Write error: %s", ec.message().c_str()); + Close(); + } +} + +void TcpSession::HandlePing(Packet* packet) { + RakNet::BitStream bitStream; + bitStream.Write(ID_CONNECTED_PONG); + bitStream.Write(packet->data[1]); + bitStream.Write(packet->data[2]); + bitStream.Write(packet->data[3]); + bitStream.Write(packet->data[4]); + bitStream.Write(0); + + Send(bitStream); +} + +void TcpSession::HandlePong(Packet* packet) { + auto current = m_PingTimer; + + RakNet::BitStream bitStream(packet->data, packet->length, false); + bitStream.IgnoreBytes(1); + + uint32_t old = 0; + bitStream.Read(old); + + m_LastPing = current - old; + m_CumulativePing += m_LastPing; + m_PingCount++; + + const auto now = std::chrono::high_resolution_clock::now(); + const auto duration = std::chrono::duration_cast(now - m_ChronoLastPingTime); + m_AccumulatedPingTime += duration; + + m_ChronoLastPongTime = now; +} + +void TcpSession::DoPing() { + if (!m_IsConnected) { + return; + } + + RakNet::BitStream bitStream; + bitStream.Write(ID_INTERNAL_PING); + bitStream.Write(m_PingTimer); + + Send(bitStream); + + m_ChronoLastPingTime = std::chrono::high_resolution_clock::now(); + + // Start the ping timer in 5 seconds using boost + m_PingTimerObject.expires_after(std::chrono::seconds(5)); + m_PingTimerObject.async_wait([this](const boost::system::error_code& ec) { + if (!ec) { + DoPing(); + } + }); +} + +void TcpSession::ReadyForUserPackets() { + m_ReadyForUserPackets = true; + + while (!m_UserPacketQueue.empty()) { + auto* packet = m_UserPacketQueue.front(); + m_UserPacketQueue.pop(); + + m_Server->OnReceive(packet); + } +} + +float TcpSession::GetAveragePing() const { + if (m_PingCount == 0) { + return 0.0f; + } + + return static_cast(m_AccumulatedPingTime.count()) / m_PingCount; +} + +float TcpSession::GetLastPing() const { + return static_cast(m_LastPing); +} + +void TcpSession::Close() { + if (!m_IsConnected) { + return; + } + + LOG("Closing connection to %s:%d", m_SystemAddress.ToString(false), m_SystemAddress.port); + + if (m_UseTls) { + m_SSLStream->async_shutdown([this](const boost::system::error_code& ec) { + if (ec) { + LOG("SSL shutdown error: %s", ec.message().c_str()); + } + + m_SSLStream->lowest_layer().close(); + + m_IsConnected = false; + + m_SSLStream.reset(); + }); + } else { + m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_Socket->close(); + + m_IsConnected = false; + + m_Socket.reset(); + } + + m_PingTimerObject.cancel(); + + m_Server->RemoveSession(m_SystemAddress); +} \ No newline at end of file diff --git a/dNet/TcpSession.h b/dNet/TcpSession.h new file mode 100644 index 00000000..fa2feea3 --- /dev/null +++ b/dNet/TcpSession.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RakNetTypes.h" +#include "eServerDisconnectIdentifiers.h" +#include "BitStream.h" + +class TcpTransportLayer; + +class TcpSession : public std::enable_shared_from_this { +public: + TcpSession(boost::asio::io_service& io_service, boost::asio::ssl::context* sslContext, TcpTransportLayer* server); + void Start(); + void Send(const std::vector& data); + void Send(const RakNet::BitStream& data); + + boost::asio::basic_socket& GetSocket(); + + bool IsConnected() const { return m_IsConnected; } + + const SystemAddress& GetSystemAddress() const { return m_SystemAddress; } + + bool IsReadyForUserPackets() const { return m_ReadyForUserPackets; } + + void ReadyForUserPackets(); + + float GetAveragePing() const; + + float GetLastPing() const; + + void Close(); + +private: + void StartAsyncRead(); + + void OnReadHeader(const boost::system::error_code& ec, std::size_t bytesTransferred); + + void OnReadBody(const boost::system::error_code& ec, std::size_t bytesTransferred); + + void OnWrite(const boost::system::error_code& ec, std::size_t bytesTransferred); + + void HandlePing(Packet* packet); + + void HandlePong(Packet* packet); + + void DoPing(); + + std::unique_ptr m_Socket; + std::unique_ptr> m_SSLStream; + bool m_UseTls; + TcpTransportLayer* m_Server; + SystemAddress m_SystemAddress; + bool m_IsConnected; + bool m_ReadyForUserPackets; + std::queue m_UserPacketQueue; + + std::vector m_HeaderBuffer; + std::vector m_BodyBuffer; + + // Ping handling + int32_t m_CumulativePing; + int32_t m_PingCount; + int32_t m_LastPing; + int32_t m_PingTimer; + boost::asio::steady_timer m_PingTimerObject; + + // Last ping time + std::chrono::time_point m_ChronoLastPingTime; + std::chrono::time_point m_ChronoLastPongTime; + // Accumulated ping time + std::chrono::milliseconds m_AccumulatedPingTime; +}; \ No newline at end of file diff --git a/dNet/TcpTransportLayer.cpp b/dNet/TcpTransportLayer.cpp new file mode 100644 index 00000000..21ceed65 --- /dev/null +++ b/dNet/TcpTransportLayer.cpp @@ -0,0 +1,426 @@ +#define _VARIADIC_MAX 10 + +#include "dNetCommon.h" +#include "Logger.h" +#include "dConfig.h" + +#include "RakNetworkFactory.h" +#include "MessageIdentifiers.h" +#include "eConnectionType.h" +#include "eServerMessageType.h" +#include "eManagerMessageType.h" + +#include "BitStreamUtils.h" +#include "MasterPackets.h" +#include "ZoneInstanceManager.h" +#include "StringifiedEnum.h" +#include "TcpTransportLayer.h" +#include "TcpSession.h" +#include "TcpPeer.h" + + +TcpTransportLayer::TcpTransportLayer( + const std::string& ip, + int port, + int instanceID, + int maxConnections, + bool isInternal, + bool useEncryption, + Logger* logger, + const std::string managerIP, + int managerPort, + ServerType serverType, + dConfig* config, + Game::signal_t* shouldShutdown, + unsigned int zoneID +) { + m_IP = ip; + m_Port = port; + m_ZoneID = zoneID; + m_InstanceID = instanceID; + m_MaxConnections = maxConnections; + m_IsInternal = isInternal; + m_UseTls = config->GetValue("use_tls") == "1"; + m_Logger = logger; + m_ManagerIP = managerIP; + m_ManagerPort = managerPort; + m_ManagerConnectionActive = false; + m_ServerType = serverType; + m_Config = config; + m_ShouldShutdown = shouldShutdown; + m_IsOkay = false; + m_ManagerConnectionActive = false; + m_ManagerTransport = nullptr; + m_SessionID = 0; + m_Acceptor = nullptr; + m_SSLContext = nullptr; +} + +TcpTransportLayer::~TcpTransportLayer() { + +} + +Packet* TcpTransportLayer::ReceiveFromManager() { + if (m_ManagerTransport == nullptr) { + throw std::runtime_error("Manager connection is not active!"); + } + + if (!m_ManagerConnectionActive) { + throw std::runtime_error("Manager connection is not active!"); + } + + auto* packet = m_ManagerTransport->Receive(); + + if (packet) { + if (packet->length < 1) { m_ManagerTransport->DeallocatePacket(packet); return nullptr; } + + if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { + LOG("Lost our connection to Manager, shutting DOWN!"); + m_ManagerConnectionActive = false; + //ConnectToManager(); //We'll just shut down now + } + + if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) { + LOG("Established connection to Manager, zone (%i), instance (%i)", this->GetZoneID(), this->GetInstanceID()); + m_ManagerConnectionActive = true; + m_ManagerSystemAddress = packet->systemAddress; + MasterPackets::SendServerInfo(this, packet); + } + + if (packet->data[0] == ID_USER_PACKET_ENUM) { + if (static_cast(packet->data[1]) == eConnectionType::MASTER) { + switch (static_cast(packet->data[3])) { + case eManagerMessageType::REQUEST_ZONE_TRANSFER_RESPONSE: { + ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(packet); + break; + } + case eManagerMessageType::SHUTDOWN: + *m_ShouldShutdown = -2; + break; + + //When we handle these packets in World instead RakNetTransportLayer, we just return the packet's pointer. + default: + + return packet; + } + } + } + + m_ManagerTransport->DeallocatePacket(packet); + } + + return nullptr; +} + +Packet* TcpTransportLayer::Receive() { + std::unique_lock lock(m_Mutex); + + if (m_ReceiveQueue.empty()) { + return nullptr; + } + + Packet* packet = m_ReceiveQueue.front(); + m_ReceiveQueue.pop(); + + return packet; +} + +void TcpTransportLayer::DeallocatePacket(Packet* packet) { + delete[] packet->data; + delete packet; +} + +void TcpTransportLayer::DeallocateManagerPacket(Packet* packet) { + if (m_ManagerTransport) { + m_ManagerTransport->DeallocatePacket(packet); + } +} + +void TcpTransportLayer::Send(RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast) { + if (broadcast) { + for (auto& [addr, session] : m_ClientSessions) { + session->Send(bitStream); + } + + return; + } + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) { + LOG("Invalid system address!"); + return; + } + + const auto& it = m_ClientSessions.find(sysAddr); + + if (it == m_ClientSessions.end()) { + LOG("Can not send to %s:%d, client not found!", sysAddr.ToString(false), sysAddr.port); + return; + } + + it->second->Send(bitStream); +} + +void TcpTransportLayer::SendToManager(RakNet::BitStream& bitStream) { + if (!m_ManagerConnectionActive) { + throw std::runtime_error("Manager connection is not active!"); + } + + if (m_ManagerTransport) { + m_ManagerTransport->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, m_ManagerSystemAddress, false); + } +} + +void TcpTransportLayer::Disconnect(const SystemAddress& sysAddr, eServerDisconnectIdentifiers disconNotifyID) { + const auto& it = m_ClientSessions.find(sysAddr); + + if (it == m_ClientSessions.end()) { + LOG("Client %s:%d not found in the session map!", sysAddr.ToString(false), sysAddr.port); + return; + } + + RakNet::BitStream bitStream; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::DISCONNECT_NOTIFY); + bitStream.Write(disconNotifyID); + + it->second->Send(bitStream); + + m_ClientSessions.erase(it); +} + +bool TcpTransportLayer::IsConnected(const SystemAddress& sysAddr) { + return m_ClientSessions.find(sysAddr) != m_ClientSessions.end(); +} + +bool TcpTransportLayer::Startup() { + m_VerifyCertificate = m_Config->GetValue("verify_certificate") == "1"; + + if (m_UseTls) { + m_SSLContext = std::make_unique(boost::asio::ssl::context::sslv23); + m_SSLContext->set_verify_mode(m_VerifyCertificate ? boost::asio::ssl::verify_peer : boost::asio::ssl::verify_none); + + if (m_Config->GetValue("cert_file").empty() || m_Config->GetValue("key_file").empty()) { + LOG("Missing certificate or key file for TLS connection!"); + return false; + } + + m_SSLContext->use_certificate_chain_file(m_Config->GetValue("cert_file")); + m_SSLContext->use_private_key_file(m_Config->GetValue("key_file"), boost::asio::ssl::context::pem); + } + + m_Acceptor = std::make_unique(m_IOService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), m_Port)); + + StartAccept(); + + // Start the I/O service in a separate thread + m_IOThread = std::thread([this]() { + m_IOService.run(); + }); + + if (m_ServerType != ServerType::Manager) { + SetupForManagerConnection(); + if (!ConnectToManager()) { + return false; + } + } + + return true; +} + +void TcpTransportLayer::Shutdown() { + // Disconnect all clients + std::vector addresses; + for (auto& [addr, session] : m_ClientSessions) { + addresses.push_back(addr); + } + + for (auto& addr : addresses) { + Disconnect(addr, eServerDisconnectIdentifiers::SERVER_SHUTDOWN); + } + + m_IOService.stop(); + + try { + if (m_IOThread.joinable()) { + m_IOThread.join(); + } + } + catch (const std::exception& e) { + LOG("Exception in io-thread: %s", e.what()); + } + + m_Acceptor->close(); +} + +void TcpTransportLayer::RemoveSession(const SystemAddress& sysAddr) { + m_ClientSessions.erase(sysAddr); +} + +void TcpTransportLayer::OnReceive(Packet* packet) { + const auto& it = m_ClientSessions.find(packet->systemAddress); + + if (it == m_ClientSessions.end()) { + LOG("Client %s:%d not found in the session map!", packet->systemAddress.ToString(false), packet->systemAddress.port); + return; + } + + const auto& session = it->second; + + const auto& type = packet->data[0]; + + if (type == ID_USER_PACKET_ENUM) { + std::unique_lock lock(m_Mutex); + + m_ReceiveQueue.push(packet); + + return; + } + + if (type == ID_DISCONNECTION_NOTIFICATION) { + LOG("A client has disconnected"); + session->Close(); + RemoveSession(packet->systemAddress); + } else if (type == ID_CONNECTION_LOST) { + LOG("Lost our connection to a client"); + session->Close(); + RemoveSession(packet->systemAddress); + } else if (type == ID_NEW_INCOMING_CONNECTION) { + LOG("New incoming connection from %s:%d", packet->systemAddress.ToString(false), packet->systemAddress.port); + } else if (type == ID_CONNECTION_REQUEST) { + LOG("Connection request from %s:%d", packet->systemAddress.ToString(false), packet->systemAddress.port); + + const std::string password = std::string(reinterpret_cast(packet->data + 1), packet->length - 1); + + if (m_IsInternal) { + if (password != NET_PASSWORD_INTERNAL) { + LOG("Invalid internal password from %s:%d", packet->systemAddress.ToString(false), packet->systemAddress.port); + Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::WRONG_GAME_VERSION); + return; + } + } + else { + if (password != NET_PASSWORD_EXTERNAL) { + LOG("Invalid external password from %s:%d", packet->systemAddress.ToString(false), packet->systemAddress.port); + Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::WRONG_GAME_VERSION); + return; + } + } + + RakNet::BitStream bitStream; + bitStream.Write(ID_CONNECTION_REQUEST_ACCEPTED); + bitStream.Write(packet->systemAddress.binaryAddress); + bitStream.Write(packet->systemAddress.port); + bitStream.Write(0); + // Our ip and port + bitStream.Write(inet_addr(m_IP.c_str())); + bitStream.Write(m_Port); + + Send(bitStream, packet->systemAddress, false); + + session->ReadyForUserPackets(); + } +} + +void TcpTransportLayer::SetupForManagerConnection() { + +} + +bool TcpTransportLayer::ConnectToManager() { + if (m_ManagerTransport) { + delete m_ManagerTransport; + m_ManagerTransport = nullptr; + } + + m_ManagerTransport = new TcpPeer(m_ManagerIP, m_ManagerPort, NET_PASSWORD_INTERNAL, m_UseTls, m_VerifyCertificate); + + if (!m_ManagerTransport->WaitForConnection()) { + LOG("Failed to connect to Manager!"); + return false; + } + + if (!m_ManagerTransport->IsConnected()) { + LOG("Failed to connect to Manager!"); + return false; + } + + m_ManagerConnectionActive = true; + + LOG("Connected to Manager %s:%d", m_ManagerIP.c_str(), m_ManagerPort); + + return true; +} + +void TcpTransportLayer::StartAccept() { + LOG("Listening for incoming connection on %s:%d", m_IP.c_str(), m_Port); + + auto session = std::make_shared(m_IOService, m_SSLContext.get(), this); + + m_Acceptor->async_accept(session->GetSocket(), + [this, session](const boost::system::error_code& ec) { + HandleAccept(session, ec); + } + ); +} + +void TcpTransportLayer::HandleAccept(const std::shared_ptr& session, const boost::system::error_code& ec) { + if (!ec) { + session->Start(); + + SystemAddress sysAddr = session->GetSystemAddress(); + + if (m_ClientSessions.find(sysAddr) != m_ClientSessions.end()) { + LOG("Client already exists in the session map!"); + return; + } + + m_ClientSessions[sysAddr] = session; + + LOG("[%i] Accepted connection from %s:%d", static_cast(m_ServerType), session->GetSocket().remote_endpoint().address().to_string().c_str(), session->GetSocket().remote_endpoint().port()); + } else { + LOG("Accept error: %s", ec.message().c_str()); + } + + StartAccept(); +} + +int TcpTransportLayer::GetPing(const SystemAddress& sysAddr) const { + const auto& it = m_ClientSessions.find(sysAddr); + + if (it == m_ClientSessions.end()) { + LOG("Client %s:%d not found in the session map!", sysAddr.ToString(false), sysAddr.port); + return 0; + } + + return it->second->GetAveragePing(); +} + +int TcpTransportLayer::GetLatestPing(const SystemAddress& sysAddr) const { + const auto& it = m_ClientSessions.find(sysAddr); + + if (it == m_ClientSessions.end()) { + LOG("Client %s:%d not found in the session map!", sysAddr.ToString(false), sysAddr.port); + return 0; + } + + return it->second->GetLastPing(); +} + +TransportPeerInterface* TcpTransportLayer::CreateOutgoingTransport( + uint32_t peerPort, + const std::string& ip, + uint32_t port, + const std::string& password +) const { + auto* peer = new TcpPeer(ip, port, password, m_UseTls, m_VerifyCertificate); + + if (!peer->WaitForConnection()) { + LOG("Failed to connect to %s:%d", ip.c_str(), port); + delete peer; + throw std::runtime_error("Failed to connect to the server!"); + } + + return peer; +} + +uint32_t TcpTransportLayer::ClaimSessionID() { + return ++m_SessionID; +} \ No newline at end of file diff --git a/dNet/TcpTransportLayer.h b/dNet/TcpTransportLayer.h new file mode 100644 index 00000000..673591fb --- /dev/null +++ b/dNet/TcpTransportLayer.h @@ -0,0 +1,158 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "RakPeerInterface.h" +#include "ReplicaManager.h" +#include "NetworkIDManager.h" + +#include "TransportLayer.h" +#include "TransportPeerInterface.h" +#include "TcpSession.h" + +namespace std { + // SystemAddress hash specialization + template <> + struct hash { + std::size_t operator()(const SystemAddress& sysAddr) const { + const std::size_t hash1 = sysAddr.binaryAddress; + const std::size_t hash2 = sysAddr.port; + return (hash1 | (hash2 << 32)); + } + }; +} + +class TcpPeer; + +class TcpTransportLayer : public TransportLayer { +public: + TcpTransportLayer( + const std::string& ip, + int port, + int instanceID, + int maxConnections, + bool isInternal, + bool useEncryption, + Logger* logger, + const std::string managerIP, + int managerPort, + ServerType serverType, + dConfig* config, + Game::signal_t* shouldShutdown, + unsigned int zoneID = 0 + ); + + ~TcpTransportLayer(); + + Packet* ReceiveFromManager() override; + + Packet* Receive() override; + + void DeallocatePacket(Packet* packet) override; + + void DeallocateManagerPacket(Packet* packet) override; + + void Send(RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast) override; + + void SendToManager(RakNet::BitStream& bitStream) override; + + void Disconnect(const SystemAddress& sysAddr, eServerDisconnectIdentifiers disconNotifyID) override; + + bool IsConnected(const SystemAddress& sysAddr) override; + + const std::string& GetIP() const override { return m_IP; } + + const int GetPort() const override { return m_Port; } + + const int GetMaxConnections() const override { return m_MaxConnections; } + + const bool GetIsEncrypted() const override { return m_UseTls; } + + const bool GetIsInternal() const override { return m_IsInternal; } + + const bool GetIsOkay() const override { return m_IsOkay; } + + Logger* GetLogger() const override { return m_Logger; } + + const bool GetIsConnectedToManager() const override { return m_ManagerConnectionActive; } + + const unsigned int GetZoneID() const override { return m_ZoneID; } + + const int GetInstanceID() const override { return m_InstanceID; } + + int GetPing(const SystemAddress& sysAddr) const override; + + int GetLatestPing(const SystemAddress& sysAddr) const override; + + const ServerType GetServerType() const override { return m_ServerType; } + + TransportPeerInterface* CreateOutgoingTransport( + uint32_t peerPort, + const std::string& ip, + uint32_t port, + const std::string& password + ) const override; + + bool Startup() override; + + void Shutdown() override; + + void OnReceive(Packet* packet); + + void RemoveSession(const SystemAddress& sysAddr); + + uint32_t ClaimSessionID(); + +private: + void SetupForManagerConnection(); + + bool ConnectToManager(); + +private: + void StartAccept(); + + void HandleAccept(const std::shared_ptr& session, const boost::system::error_code& ec); + + boost::asio::io_service m_IOService; + std::unique_ptr m_Acceptor; + std::unique_ptr m_SSLContext; + bool m_UseTls; + bool m_VerifyCertificate; + + std::unordered_map> m_ClientSessions; + std::queue m_ReceiveQueue; + std::mutex m_Mutex; + std::thread m_IOThread; + + Logger* m_Logger = nullptr; + dConfig* m_Config = nullptr; + + Game::signal_t* m_ShouldShutdown = nullptr; + + std::string m_IP; + int32_t m_Port; + int32_t m_MaxConnections; + uint32_t m_ZoneID; + int32_t m_InstanceID; + bool m_IsInternal; + bool m_IsOkay; + bool m_ManagerConnectionActive; + ServerType m_ServerType; + int32_t m_SessionID = 0; + + TcpPeer* m_ManagerTransport = nullptr; + SystemAddress m_ManagerSystemAddress; + std::string m_ManagerIP; + int m_ManagerPort; +}; diff --git a/dNet/TransportLayer.h b/dNet/TransportLayer.h new file mode 100644 index 00000000..a45e86d5 --- /dev/null +++ b/dNet/TransportLayer.h @@ -0,0 +1,193 @@ +#pragma once +#include +#include + +#include "RakNetTypes.h" +#include "TransportPeerInterface.h" + +class Logger; +class dConfig; +enum class eServerDisconnectIdentifiers : uint32_t; + +enum class ServerType : uint32_t { + Manager, + Auth, + Chat, + World +}; + +enum class ServiceId : uint32_t { + General = 0, + Auth = 1, + Chat = 2, + World = 4, + Client = 5, +}; + +namespace Game { + using signal_t = volatile std::sig_atomic_t; +} + +/** + * @brief Abstract base class for managing the network and transport layer. + */ +class TransportLayer { +public: + /** + * @brief Receives a packet from the manager. + * @return Pointer to the received Packet. + */ + virtual Packet* ReceiveFromManager() = 0; + + /** + * @brief Deallocates a packet received from the manager. + * @param packet Pointer to the Packet to be deallocated. + */ + virtual void DeallocateManagerPacket(Packet* packet) = 0; + + /** + * @brief Sends a bit stream to the manager. + * @param bitStream Reference to the bitsteam to be sent. + */ + virtual void SendToManager(RakNet::BitStream& bitStream) = 0; + + /** + * @brief Receives a packet. + * @return Pointer to the received Packet. + */ + virtual Packet* Receive() = 0; + + /** + * @brief Deallocates a received packet. + * @param packet Pointer to the Packet to be deallocated. + */ + virtual void DeallocatePacket(Packet* packet) = 0; + + /** + * @brief Sends a bitstream to a specified system address. + * @param bitStream Reference to the RakNet::BitStream to be sent. + * @param sysAddr Reference to the SystemAddress to send the bit stream to. + * @param broadcast Whether to broadcast the bit stream. + */ + virtual void Send(RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast) = 0; + + /** + * @brief Disconnects from a specified system address. + * @param sysAddr Reference to the SystemAddress to disconnect from. + * @param disconNotifyID Identifier for the disconnection notification. + */ + virtual void Disconnect(const SystemAddress& sysAddr, eServerDisconnectIdentifiers disconNotifyID) = 0; + + /** + * @brief Checks if connected to a specified system address. + * @param sysAddr Reference to the SystemAddress to check connection status. + * @return Connection status, true if connected, false otherwise. + */ + virtual bool IsConnected(const SystemAddress& sysAddr) = 0; + + /** + * @brief Gets the IP address. + * @return Reference to the IP address string. + */ + virtual const std::string& GetIP() const = 0; + + /** + * @brief Gets the port number. + * @return The port number. + */ + virtual const int GetPort() const = 0; + + /** + * @brief Gets the maximum number of connections. + * @return The maximum number of connections. + */ + virtual const int GetMaxConnections() const = 0; + + /** + * @brief Checks if the connection is encrypted. + * @return Weather the connection is encrypted. + */ + virtual const bool GetIsEncrypted() const = 0; + + /** + * @brief Checks if the connection is internal. + * @return Weather the connection is internal. + */ + virtual const bool GetIsInternal() const = 0; + + /** + * @brief Checks if the connection is okay. + * @return Weather the connection is okay. + */ + virtual const bool GetIsOkay() const = 0; + + /** + * @brief Gets the logger instance. + * @return Pointer to the Logger instance. + */ + virtual Logger* GetLogger() const = 0; + + /** + * @brief Checks if connected to the manager. + * @return Weather we are connected to the manager. + */ + virtual const bool GetIsConnectedToManager() const = 0; + + /** + * @brief Gets the zone ID. + * @return The zone ID. + */ + virtual const uint32_t GetZoneID() const = 0; + + /** + * @brief Gets the instance ID. + * @return The instance ID. + */ + virtual const int GetInstanceID() const = 0; + + /** + * @brief Gets the ping to a specified system address. + * @param sysAddr Reference to the SystemAddress to get the ping for. + * @return The ping. + */ + virtual int GetPing(const SystemAddress& sysAddr) const = 0; + + /** + * @brief Gets the latest ping to a specified system address. + * @param sysAddr Reference to the SystemAddress to get the latest ping for. + * @return The latest ping. + */ + virtual int GetLatestPing(const SystemAddress& sysAddr) const = 0; + + /** + * @brief Gets the server type. + * @return The server type. + */ + virtual const ServerType GetServerType() const = 0; + + /** + * @brief Starts up the transport layer. + * + * @return True if successful, false otherwise. + */ + virtual bool Startup() = 0; + + /** + * @brief Shuts down the transport layer. + */ + virtual void Shutdown() = 0; + + /** + * @brief Create an outgoing transport connected to a server. + * + * @param ip The IP address of the server. + * @param port The port of the server. + * @param password The password to connect to the server. + */ + virtual TransportPeerInterface* CreateOutgoingTransport( + uint32_t peerPort, + const std::string& ip, + uint32_t port, + const std::string& password + ) const = 0; +}; diff --git a/dNet/TransportPeerInterface.h b/dNet/TransportPeerInterface.h new file mode 100644 index 00000000..c4ea68fc --- /dev/null +++ b/dNet/TransportPeerInterface.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "RakNetTypes.h" +#include "PacketPriority.h" + +class TransportPeerInterface { +public: + virtual void Send( + const RakNet::BitStream* bitStream, + PacketPriority priority, + PacketReliability reliability, + char orderingChannel, + SystemAddress systemAddress, + bool broadcas + ) = 0; + + virtual void Disconnect() = 0; + + virtual void Reconnect() = 0; + + virtual Packet* Receive() = 0; + + virtual void DeallocatePacket(Packet* packet) = 0; +}; diff --git a/dNet/ZoneInstanceManager.cpp b/dNet/ZoneInstanceManager.cpp index 354d3634..6df1ccd9 100644 --- a/dNet/ZoneInstanceManager.cpp +++ b/dNet/ZoneInstanceManager.cpp @@ -20,7 +20,7 @@ void ZoneInstanceManager::RequestZoneTransfer(dServer* server, uint32_t zoneID, this->requests.push_back(request); - MasterPackets::SendZoneTransferRequest(server, request->requestID, mythranShift, zoneID, zoneClone); + MasterPackets::SendZoneTransferRequest(server->GetTransportLayerPtr(), request->requestID, mythranShift, zoneID, zoneClone); } //! Handles a zone transfer response @@ -58,7 +58,7 @@ void ZoneInstanceManager::HandleRequestZoneTransferResponse(Packet* packet) { } void ZoneInstanceManager::CreatePrivateZone(dServer* server, uint32_t zoneID, uint32_t zoneClone, const std::string& password) { - MasterPackets::SendZoneCreatePrivate(server, zoneID, zoneClone, password); + MasterPackets::SendZoneCreatePrivate(server->GetTransportLayerPtr(), zoneID, zoneClone, password); } void ZoneInstanceManager::RequestPrivateZone( @@ -72,5 +72,5 @@ void ZoneInstanceManager::RequestPrivateZone( this->requests.push_back(request); - MasterPackets::SendZoneRequestPrivate(server, request->requestID, mythranShift, password); + MasterPackets::SendZoneRequestPrivate(server->GetTransportLayerPtr(), request->requestID, mythranShift, password); } diff --git a/dNet/dServer.cpp b/dNet/dServer.cpp index e504a985..8fcf721d 100644 --- a/dNet/dServer.cpp +++ b/dNet/dServer.cpp @@ -8,252 +8,187 @@ #include "MessageIdentifiers.h" #include "eConnectionType.h" #include "eServerMessageType.h" -#include "eMasterMessageType.h" +#include "eManagerMessageType.h" #include "BitStreamUtils.h" #include "MasterPackets.h" #include "ZoneInstanceManager.h" #include "StringifiedEnum.h" -//! Replica Constructor class -class ReplicaConstructor : public ReceiveConstructionInterface { -public: - ReplicaReturnResult ReceiveConstruction(RakNet::BitStream* inBitStream, RakNetTime timestamp, NetworkID networkID, NetworkIDObject* existingObject, SystemAddress senderId, ReplicaManager* caller) { - return REPLICA_PROCESSING_DONE; +#include "RakNetTransportLayer.h" +#ifdef NET_ENABLE_TCP_TRANSPORT +#include "TcpTransportLayer.h" +#endif + +dServer::dServer( + const std::string& ip, + int port, + int instanceID, + int maxConnections, + bool isInternal, + bool useEncryption, + Logger* logger, + const std::string masterIP, + int masterPort, + ServerType serverType, + dConfig* config, + Game::signal_t* shouldShutdown, + unsigned int zoneID +) { + m_TransportType = TransportType::RakNet; + + if (config->GetValue("transport_layer") == "tcp") { + m_TransportType = TransportType::Tcp; + LOG("Using TCP transport layer."); } -} ConstructionCB; - -//! Replica Download Sender class -class ReplicaSender : public SendDownloadCompleteInterface { -public: - ReplicaReturnResult SendDownloadComplete(RakNet::BitStream* outBitStream, RakNetTime currentTime, SystemAddress senderId, ReplicaManager* caller) { - return REPLICA_PROCESSING_DONE; - } -} SendDownloadCompleteCB; - -//! Replica Download Receiver class -class ReplicaReceiever : public ReceiveDownloadCompleteInterface { -public: - ReplicaReturnResult ReceiveDownloadComplete(RakNet::BitStream* inBitStream, SystemAddress senderId, ReplicaManager* caller) { - return REPLICA_PROCESSING_DONE; - } -} ReceiveDownloadCompleteCB; - -dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) { - mIP = ip; - mPort = port; - mZoneID = zoneID; - mInstanceID = instanceID; - mMaxConnections = maxConnections; - mIsInternal = isInternal; - mUseEncryption = useEncryption; - mLogger = logger; - mMasterIP = masterIP; - mMasterPort = masterPort; - mMasterConnectionActive = false; - mNetIDManager = nullptr; - mReplicaManager = nullptr; - mServerType = serverType; - mConfig = config; - mShouldShutdown = lastSignal; - //Attempt to start our server here: - mIsOkay = Startup(); - - //Forcibly log to both the console and our file what ip, port and possibly zoneID / instanceID we're running on: - bool prevLogSetting = mLogger->GetLogToConsole(); - mLogger->SetLogToConsole(true); - - if (mIsOkay) { - if (zoneID == 0) - LOG("%s Server is listening on %s:%i with encryption: %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption)); - else - LOG("%s Server is listening on %s:%i with encryption: %i, running zone %i / %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption), zoneID, instanceID); - } else { LOG("FAILED TO START SERVER ON IP/PORT: %s:%i", ip.c_str(), port); return; } - - mLogger->SetLogToConsole(prevLogSetting); - - //Connect to master if we are not master: - if (serverType != ServerType::Master) { - SetupForMasterConnection(); - if (!ConnectToMaster()) { - LOG("Failed ConnectToMaster!"); - } + else { + LOG("Using RakNet transport layer."); } - //Set up Replica if we're a world server: - if (serverType == ServerType::World) { - mNetIDManager = new NetworkIDManager(); - mNetIDManager->SetIsNetworkIDAuthority(true); + switch (m_TransportType) { + case TransportType::RakNet: + m_TransportLayer = std::make_unique( + ip, + port, + instanceID, + maxConnections, + isInternal, + useEncryption, + logger, + masterIP, + masterPort, + serverType, + config, + shouldShutdown, + zoneID + ); + break; + case TransportType::Tcp: +#ifdef NET_ENABLE_TCP_TRANSPORT + m_TransportLayer = std::make_unique( + ip, + port, + instanceID, + maxConnections, + isInternal, + useEncryption, + logger, + masterIP, + masterPort, + serverType, + config, + shouldShutdown, + zoneID + ); +#else + throw std::runtime_error("TCP transport is not enabled!"); +#endif + break; + } - mReplicaManager = new ReplicaManager(); - mReplicaManager->SetAutoParticipateNewConnections(false); - mReplicaManager->SetAutoConstructToNewParticipants(false); - mReplicaManager->SetAutoSerializeInScope(true); - mReplicaManager->SetReceiveConstructionCB(&ConstructionCB); - mReplicaManager->SetDownloadCompleteCB(&SendDownloadCompleteCB, &ReceiveDownloadCompleteCB); + bool okey = m_TransportLayer->Startup(); - mPeer->AttachPlugin(mReplicaManager); - mPeer->SetNetworkIDManager(mNetIDManager); + if (!okey) { + LOG("Failed to start the server!"); + + throw std::runtime_error("Failed to start the server!"); } } dServer::~dServer() { - Shutdown(); + m_TransportLayer->Shutdown(); + + m_TransportLayer = nullptr; } Packet* dServer::ReceiveFromMaster() { - if (!mMasterPeer) return nullptr; - if (!mMasterConnectionActive) ConnectToMaster(); - - Packet* packet = mMasterPeer->Receive(); - if (packet) { - if (packet->length < 1) { mMasterPeer->DeallocatePacket(packet); return nullptr; } - - if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { - LOG("Lost our connection to master, shutting DOWN!"); - mMasterConnectionActive = false; - //ConnectToMaster(); //We'll just shut down now - } - - if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) { - LOG("Established connection to master, zone (%i), instance (%i)", this->GetZoneID(), this->GetInstanceID()); - mMasterConnectionActive = true; - mMasterSystemAddress = packet->systemAddress; - MasterPackets::SendServerInfo(this, packet); - } - - if (packet->data[0] == ID_USER_PACKET_ENUM) { - if (static_cast(packet->data[1]) == eConnectionType::MASTER) { - switch (static_cast(packet->data[3])) { - case eMasterMessageType::REQUEST_ZONE_TRANSFER_RESPONSE: { - ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(packet); - break; - } - case eMasterMessageType::SHUTDOWN: - *mShouldShutdown = -2; - break; - - //When we handle these packets in World instead dServer, we just return the packet's pointer. - default: - - return packet; - } - } - } - - mMasterPeer->DeallocatePacket(packet); - } - - return nullptr; + return m_TransportLayer->ReceiveFromManager(); } Packet* dServer::Receive() { - return mPeer->Receive(); + return m_TransportLayer->Receive(); } void dServer::DeallocatePacket(Packet* packet) { - mPeer->DeallocatePacket(packet); + m_TransportLayer->DeallocatePacket(packet); } void dServer::DeallocateMasterPacket(Packet* packet) { - mMasterPeer->DeallocatePacket(packet); + m_TransportLayer->DeallocateManagerPacket(packet); } void dServer::Send(RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast) { - mPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, sysAddr, broadcast); + m_TransportLayer->Send(bitStream, sysAddr, broadcast); } void dServer::SendToMaster(RakNet::BitStream& bitStream) { - if (!mMasterConnectionActive) ConnectToMaster(); - mMasterPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, mMasterSystemAddress, false); + m_TransportLayer->SendToManager(bitStream); } void dServer::Disconnect(const SystemAddress& sysAddr, eServerDisconnectIdentifiers disconNotifyID) { - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::DISCONNECT_NOTIFY); - bitStream.Write(disconNotifyID); - mPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, sysAddr, false); - - mPeer->CloseConnection(sysAddr, true); + m_TransportLayer->Disconnect(sysAddr, disconNotifyID); } bool dServer::IsConnected(const SystemAddress& sysAddr) { - return mPeer->IsConnected(sysAddr); -} - -bool dServer::Startup() { - mSocketDescriptor = SocketDescriptor(uint16_t(mPort), 0); - mPeer = RakNetworkFactory::GetRakPeerInterface(); - - if (!mPeer) return false; - if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false; - - if (mIsInternal) { - mPeer->SetIncomingPassword("3.25 DARKFLAME1", 15); - } else { - UpdateBandwidthLimit(); - UpdateMaximumMtuSize(); - mPeer->SetIncomingPassword("3.25 ND1", 8); - } - - mPeer->SetMaximumIncomingConnections(mMaxConnections); - if (mUseEncryption) mPeer->InitializeSecurity(NULL, NULL, NULL, NULL); - - return true; -} - -void dServer::UpdateMaximumMtuSize() { - auto maxMtuSize = mConfig->GetValue("maximum_mtu_size"); - mPeer->SetMTUSize(maxMtuSize.empty() ? 1228 : std::stoi(maxMtuSize)); -} - -void dServer::UpdateBandwidthLimit() { - auto newBandwidth = mConfig->GetValue("maximum_outgoing_bandwidth"); - mPeer->SetPerConnectionOutgoingBandwidthLimit(!newBandwidth.empty() ? std::stoi(newBandwidth) : 0); -} - -void dServer::Shutdown() { - if (mPeer) { - mPeer->Shutdown(1000); - RakNetworkFactory::DestroyRakPeerInterface(mPeer); - } - - if (mNetIDManager) { - delete mNetIDManager; - mNetIDManager = nullptr; - } - - if (mReplicaManager) { - delete mReplicaManager; - mReplicaManager = nullptr; - } - - if (mServerType != ServerType::Master && mMasterPeer) { - mMasterPeer->Shutdown(1000); - RakNetworkFactory::DestroyRakPeerInterface(mMasterPeer); - } -} - -void dServer::SetupForMasterConnection() { - mMasterSocketDescriptor = SocketDescriptor(uint16_t(mPort + 1), 0); - mMasterPeer = RakNetworkFactory::GetRakPeerInterface(); - bool ret = mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1); - if (!ret) LOG("Failed MasterPeer Startup!"); -} - -bool dServer::ConnectToMaster() { - //LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort); - return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15); -} - -void dServer::UpdateReplica() { - mReplicaManager->Update(mPeer); + return m_TransportLayer->IsConnected(sysAddr); } int dServer::GetPing(const SystemAddress& sysAddr) const { - return mPeer->GetAveragePing(sysAddr); + return m_TransportLayer->GetPing(sysAddr); } int dServer::GetLatestPing(const SystemAddress& sysAddr) const { - return mPeer->GetLastPing(sysAddr); + return m_TransportLayer->GetLatestPing(sysAddr); +} + +const std::string& dServer::GetIP() const { + return m_TransportLayer->GetIP(); +} + +const int dServer::GetPort() const { + return m_TransportLayer->GetPort(); +} + +const int dServer::GetMaxConnections() const { + return m_TransportLayer->GetMaxConnections(); +} + +const bool dServer::GetIsEncrypted() const { + return m_TransportLayer->GetIsEncrypted(); +} + +const bool dServer::GetIsInternal() const { + return m_TransportLayer->GetIsInternal(); +} + +const bool dServer::GetIsOkay() const { + return m_TransportLayer->GetIsOkay(); +} + +Logger* dServer::GetLogger() const { + return m_TransportLayer->GetLogger(); +} + +const bool dServer::GetIsConnectedToMaster() const { + return m_TransportLayer->GetIsConnectedToManager(); +} + +const unsigned int dServer::GetZoneID() const { + return m_TransportLayer->GetZoneID(); +} + +const int dServer::GetInstanceID() const { + return m_TransportLayer->GetInstanceID(); +} + +const ServerType dServer::GetServerType() const { + return m_TransportLayer->GetServerType(); +} + +const std::unique_ptr& dServer::GetTransportLayer() const { + return m_TransportLayer; +} + +const TransportType dServer::GetTransportType() const { + return m_TransportType; } diff --git a/dNet/dServer.h b/dNet/dServer.h index 40f606f1..3ce05d95 100644 --- a/dNet/dServer.h +++ b/dNet/dServer.h @@ -1,33 +1,15 @@ #pragma once #include #include -#include "RakPeerInterface.h" -#include "ReplicaManager.h" -#include "NetworkIDManager.h" +#include -class Logger; -class dConfig; -enum class eServerDisconnectIdentifiers : uint32_t; +#include "TransportLayer.h" -enum class ServerType : uint32_t { - Master, - Auth, - Chat, - World +enum class TransportType : uint32_t { + RakNet, + Tcp }; -enum class ServiceId : uint32_t{ - General = 0, - Auth = 1, - Chat = 2, - World = 4, - Client = 5, -}; - -namespace Game { - using signal_t = volatile std::sig_atomic_t; -} - class dServer { public: // Default constructor should only used for testing! @@ -45,73 +27,85 @@ public: ServerType serverType, dConfig* config, Game::signal_t* shouldShutdown, - unsigned int zoneID = 0); + unsigned int zoneID = 0 + ); + ~dServer(); Packet* ReceiveFromMaster(); + Packet* Receive(); + void DeallocatePacket(Packet* packet); + void DeallocateMasterPacket(Packet* packet); + virtual void Send(RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast); + void SendToMaster(RakNet::BitStream& bitStream); void Disconnect(const SystemAddress& sysAddr, eServerDisconnectIdentifiers disconNotifyID); bool IsConnected(const SystemAddress& sysAddr); - const std::string& GetIP() const { return mIP; } - const int GetPort() const { return mPort; } - const int GetMaxConnections() const { return mMaxConnections; } - const bool GetIsEncrypted() const { return mUseEncryption; } - const bool GetIsInternal() const { return mIsInternal; } - const bool GetIsOkay() const { return mIsOkay; } - Logger* GetLogger() const { return mLogger; } - const bool GetIsConnectedToMaster() const { return mMasterConnectionActive; } - const unsigned int GetZoneID() const { return mZoneID; } - const int GetInstanceID() const { return mInstanceID; } - ReplicaManager* GetReplicaManager() { return mReplicaManager; } - void UpdateReplica(); - void UpdateBandwidthLimit(); - void UpdateMaximumMtuSize(); + + const std::string& GetIP() const; + + const int GetPort() const; + + const int GetMaxConnections() const; + + const bool GetIsEncrypted() const; + + const bool GetIsInternal() const; + + const bool GetIsOkay() const; + + Logger* GetLogger() const; + + const bool GetIsConnectedToMaster() const; + + const unsigned int GetZoneID() const; + + const int GetInstanceID() const; int GetPing(const SystemAddress& sysAddr) const; + int GetLatestPing(const SystemAddress& sysAddr) const; - NetworkIDManager* GetNetworkIDManager() { return mNetIDManager; } - - const ServerType GetServerType() const { return mServerType; } - -private: - bool Startup(); - void Shutdown(); - void SetupForMasterConnection(); - bool ConnectToMaster(); - -private: - Logger* mLogger = nullptr; - dConfig* mConfig = nullptr; - RakPeerInterface* mPeer = nullptr; - ReplicaManager* mReplicaManager = nullptr; - NetworkIDManager* mNetIDManager = nullptr; + const ServerType GetServerType() const; /** - * Whether or not to shut down the server. Pointer to Game::lastSignal. + * @brief Gets the transport layer. + * + * @return The TransportLayer instance. */ - Game::signal_t* mShouldShutdown = nullptr; - SocketDescriptor mSocketDescriptor; - std::string mIP; - int mPort; - int mMaxConnections; - unsigned int mZoneID; - int mInstanceID; - bool mUseEncryption; - bool mIsInternal; - bool mIsOkay; - bool mMasterConnectionActive; - ServerType mServerType; + const std::unique_ptr& GetTransportLayer() const; - RakPeerInterface* mMasterPeer = nullptr; - SocketDescriptor mMasterSocketDescriptor; - SystemAddress mMasterSystemAddress; - std::string mMasterIP; - int mMasterPort; + /** + * @brief Gets the transport type. + * + * @return The transport type. + */ + const TransportType GetTransportType() const; + + /** + * Implicit conversion to TransportLayer*. + */ + operator TransportLayer*() const { + return m_TransportLayer.get(); + } + + /** + * @brief Get pointer to the TransportLayer. + * + * @return Pointer to the TransportLayer. + */ + TransportLayer* GetTransportLayerPtr() const { + return m_TransportLayer.get(); + } + +private: + TransportType m_TransportType; + + std::unique_ptr m_TransportLayer; }; diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 90ad132d..271ee247 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -68,7 +68,7 @@ #include "eServerMessageType.h" #include "eChatMessageType.h" #include "eWorldMessageType.h" -#include "eMasterMessageType.h" +#include "eManagerMessageType.h" #include "eGameMessageType.h" #include "ZCompression.h" #include "EntityManager.h" @@ -87,7 +87,7 @@ namespace Game { dChatFilter* chatFilter = nullptr; dConfig* config = nullptr; AssetManager* assetManager = nullptr; - RakPeerInterface* chatServer = nullptr; + TransportPeerInterface* chatServer = nullptr; std::mt19937 randomEngine; SystemAddress chatSysAddr; Game::signal_t lastSignal = 0; @@ -218,10 +218,12 @@ int main(int argc, char** argv) { uint32_t chatPort = 1501; if (Game::config->GetValue("chat_server_port") != "") chatPort = std::atoi(Game::config->GetValue("chat_server_port").c_str()); - auto chatSock = SocketDescriptor(static_cast(ourPort + 2), 0); - Game::chatServer = RakNetworkFactory::GetRakPeerInterface(); - Game::chatServer->Startup(1, 30, &chatSock, 1); - Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8); + Game::chatServer = Game::server->GetTransportLayer()->CreateOutgoingTransport( + ourPort + 2, + masterIP, + chatPort, + "3.25 ND1" + ); //Set up other things: Game::randomEngine = std::mt19937(time(0)); @@ -379,7 +381,7 @@ int main(int argc, char** argv) { if (framesSinceChatDisconnect >= chatReconnectionTime) { framesSinceChatDisconnect = 0; - Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8); + Game::chatServer->Reconnect(); } } else framesSinceChatDisconnect = 0; @@ -447,9 +449,6 @@ int main(int argc, char** argv) { Metrics::StartMeasurement(MetricVariable::UpdateReplica); - //Update our replica objects: - Game::server->UpdateReplica(); - Metrics::EndMeasurement(MetricVariable::UpdateReplica); //Push our log every 15s: @@ -510,7 +509,7 @@ int main(int argc, char** argv) { if (framesSinceMasterStatus >= 200) { LOG("Finished loading world with zone (%i), ready up!", Game::server->GetZoneID()); - MasterPackets::SendWorldReady(Game::server, Game::server->GetZoneID(), Game::server->GetInstanceID()); + MasterPackets::SendWorldReady(Game::server->GetTransportLayerPtr(), Game::server->GetZoneID(), Game::server->GetInstanceID()); ready = true; } @@ -663,8 +662,8 @@ void HandlePacketChat(Packet* packet) { void HandleMasterPacket(Packet* packet) { if (packet->length < 2) return; if (static_cast(packet->data[1]) != eConnectionType::MASTER || packet->length < 4) return; - switch (static_cast(packet->data[3])) { - case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: { + switch (static_cast(packet->data[3])) { + case eManagerMessageType::REQUEST_PERSISTENT_ID_RESPONSE: { CINSTREAM_SKIP_HEADER; uint64_t requestID; inStream.Read(requestID); @@ -674,7 +673,7 @@ void HandleMasterPacket(Packet* packet) { break; } - case eMasterMessageType::SESSION_KEY_RESPONSE: { + case eManagerMessageType::SESSION_KEY_RESPONSE: { //Read our session key and to which user it belongs: CINSTREAM_SKIP_HEADER; uint32_t sessionKey = 0; @@ -729,7 +728,7 @@ void HandleMasterPacket(Packet* packet) { //Notify master: { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PLAYER_ADDED); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::PLAYER_ADDED); bitStream.Write(Game::server->GetZoneID()); bitStream.Write(instanceID); Game::server->SendToMaster(bitStream); @@ -738,7 +737,7 @@ void HandleMasterPacket(Packet* packet) { break; } - case eMasterMessageType::AFFIRM_TRANSFER_REQUEST: { + case eManagerMessageType::AFFIRM_TRANSFER_REQUEST: { CINSTREAM_SKIP_HEADER; uint64_t requestID; inStream.Read(requestID); @@ -746,20 +745,20 @@ void HandleMasterPacket(Packet* packet) { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::AFFIRM_TRANSFER_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::AFFIRM_TRANSFER_RESPONSE); bitStream.Write(requestID); Game::server->SendToMaster(bitStream); break; } - case eMasterMessageType::SHUTDOWN: { + case eManagerMessageType::SHUTDOWN: { Game::lastSignal = -1; LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); break; } - case eMasterMessageType::NEW_SESSION_ALERT: { + case eManagerMessageType::NEW_SESSION_ALERT: { CINSTREAM_SKIP_HEADER; uint32_t sessionKey = inStream.Read(sessionKey); @@ -832,7 +831,7 @@ void HandlePacket(Packet* packet) { } CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PLAYER_REMOVED); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::PLAYER_REMOVED); bitStream.Write(Game::server->GetZoneID()); bitStream.Write(instanceID); Game::server->SendToMaster(bitStream); @@ -896,7 +895,7 @@ void HandlePacket(Packet* packet) { //Request the session info from Master: CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_SESSION_KEY); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::REQUEST_SESSION_KEY); bitStream.Write(username); Game::server->SendToMaster(bitStream); @@ -1017,8 +1016,7 @@ void HandlePacket(Packet* packet) { Character* c = user->GetLastUsedChar(); if (c != nullptr) { std::u16string username = GeneralUtils::ASCIIToUTF16(c->GetName()); - Game::server->GetReplicaManager()->AddParticipant(packet->systemAddress); - + EntityInfo info{}; info.lot = 1; Entity* player = Game::entityManager->CreateEntity(info, UserManager::Instance()->GetUser(packet->systemAddress)); @@ -1385,10 +1383,7 @@ void HandlePacket(Packet* packet) { void WorldShutdownProcess(uint32_t zoneId) { LOG("Saving map %i instance %i", zoneId, instanceID); - for (auto i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i) { - const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i); - - auto* entity = PlayerManager::GetPlayer(player); + for (auto* entity : PlayerManager::GetAllPlayers()) { LOG("Saving data!"); if (entity != nullptr && entity->GetCharacter() != nullptr) { auto* skillComponent = entity->GetComponent(); @@ -1411,8 +1406,8 @@ void WorldShutdownProcess(uint32_t zoneId) { LOG("ALL DATA HAS BEEN SAVED FOR ZONE %i INSTANCE %i!", zoneId, instanceID); - while (Game::server->GetReplicaManager()->GetParticipantCount() > 0) { - const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(0); + for (auto* entity : PlayerManager::GetAllPlayers()) { + const auto& player = entity->GetSystemAddress(); Game::server->Disconnect(player, eServerDisconnectIdentifiers::SERVER_SHUTDOWN); } @@ -1463,6 +1458,6 @@ void FinalizeShutdown() { void SendShutdownMessageToMaster() { CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eManagerMessageType::SHUTDOWN_RESPONSE); Game::server->SendToMaster(bitStream); } diff --git a/tests/dGameTests/GameDependencies.cpp b/tests/dGameTests/GameDependencies.cpp index ef58e367..3a9caa7d 100644 --- a/tests/dGameTests/GameDependencies.cpp +++ b/tests/dGameTests/GameDependencies.cpp @@ -1,5 +1,7 @@ #include "GameDependencies.h" +#include "TransportPeerInterface.h" + namespace Game { Logger* logger = nullptr; dServer* server = nullptr; @@ -7,7 +9,7 @@ namespace Game { dChatFilter* chatFilter = nullptr; dConfig* config = nullptr; std::mt19937 randomEngine; - RakPeerInterface* chatServer = nullptr; + TransportPeerInterface* chatServer = nullptr; AssetManager* assetManager = nullptr; SystemAddress chatSysAddr; EntityManager* entityManager = nullptr;