Initial work on TCP transport layer:

* Optionally compiled additional TCP transport layer.
* Config to enable it.
* Tested and functional with lcdr's tcpudp dll, udp being disabled in the dll due to port issues.
* Removed unused RakNet replica manager and id manager. We've got our own replica manager since pre-open-source.
* Utilizes async boost behavior.

Todo:
* Figure out how to do ping calculations.
* Fix crashes on universe shutdown.
* Test TLS on a VPS.
* Remove unnecessary logging.
* Test with lots of clients.
* Finish "master" to "manager" naming refactor.
This commit is contained in:
wincent 2024-10-13 22:42:59 +02:00
parent 8d54db7851
commit 4004534732
28 changed files with 2341 additions and 386 deletions

View File

@ -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;

View File

@ -1,9 +1,9 @@
#ifndef __EMASTERMESSAGETYPE__H__
#define __EMASTERMESSAGETYPE__H__
#ifndef __EMANAGERMESSAGETYPE__H__
#define __EMANAGERMESSAGETYPE__H__
#include <cstdint>
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__

View File

@ -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);
}

View File

@ -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<void(uint32_t)> 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

View File

@ -45,9 +45,10 @@
// Enums
#include "eGameMasterLevel.h"
#include "eMasterMessageType.h"
#include "eManagerMessageType.h"
#include "eInventoryType.h"
#include "ePlayerFlag.h"
#include <RakNetTransportLayer.h>
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<RakNetTransportLayer*>(transport.get());
raknetTransport->UpdateMaximumMtuSize();
raknetTransport->UpdateBandwidthLimit();
}
ChatPackets::SendSystemMessage(sysAddr, u"Successfully reloaded config for world!");
}

View File

@ -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);

View File

@ -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<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) {
switch (static_cast<eMasterMessageType>(packet->data[3])) {
case eMasterMessageType::REQUEST_PERSISTENT_ID: {
switch (static_cast<eManagerMessageType>(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<uint16_t>(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<bool>(mythranShift), zone.GetMapID(), instance->GetInstanceID(), zone.GetCloneID(), instance->GetIP(), instance->GetPort());
MasterPackets::SendZoneTransferResponse(Game::server->GetTransportLayerPtr(), packet->systemAddress, requestID, static_cast<bool>(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");
}

View File

@ -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);

View File

@ -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"

View File

@ -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 <string>
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<uint8_t>(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<char>(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<uint8_t>(mythranShift);
@ -64,22 +64,22 @@ void MasterPackets::SendZoneRequestPrivate(dServer* server, uint64_t requestID,
bitStream.Write<char>(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<uint8_t>(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);
}

View File

@ -5,23 +5,23 @@
#include <string>
#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);
}

48
dNet/RakNetPeer.cpp Normal file
View File

@ -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);
}

32
dNet/RakNetPeer.h Normal file
View File

@ -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;
};

View File

@ -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<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) {
switch (static_cast<eManagerMessageType>(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<uint16_t>(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);
}

103
dNet/RakNetTransportLayer.h Normal file
View File

@ -0,0 +1,103 @@
#pragma once
#include <string>
#include <csignal>
#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;
};

269
dNet/TcpPeer.cpp Normal file
View File

@ -0,0 +1,269 @@
#include "TcpPeer.h"
#include <RakNetTypes.h>
#include <BitStream.h>
#include <random>
#include <MessageIdentifiers.h>
#include <magic_enum.hpp>
#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>(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<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(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<boost::asio::ip::tcp::socket>(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<uint8_t>(ID_CONNECTION_REQUEST);
for (const auto& c : m_Password) {
bitStream.Write<uint8_t>(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<std::mutex> 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<uint8_t> 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<std::mutex> 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;
}

73
dNet/TcpPeer.h Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <thread>
#include <mutex>
#include <queue>
#include <memory>
#include <condition_variable>
#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<boost::asio::ip::tcp::socket> m_Socket;
std::unique_ptr<boost::asio::ssl::context> m_SSLContext;
std::unique_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> 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<Packet*> m_ReceiveBuffer;
std::vector<uint8_t> m_HeaderBuffer;
std::vector<uint8_t> m_BodyBuffer;
std::thread m_IOThread;
boost::asio::io_service::work m_Work;
};

307
dNet/TcpSession.cpp Normal file
View File

@ -0,0 +1,307 @@
#include "TcpSession.h"
#include "TcpTransportLayer.h"
#include "Logger.h"
#include "Game.h"
#include "MessageIdentifiers.h"
#include <boost/endian/conversion.hpp>
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<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(io_service, *sslContext);
}
else {
m_Socket = std::make_unique<boost::asio::ip::tcp::socket>(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<uint8_t>& 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<boost::asio::ip::tcp, boost::asio::any_io_executor>& 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<uint8_t>(ID_CONNECTED_PONG);
bitStream.Write<uint8_t>(packet->data[1]);
bitStream.Write<uint8_t>(packet->data[2]);
bitStream.Write<uint8_t>(packet->data[3]);
bitStream.Write<uint8_t>(packet->data[4]);
bitStream.Write<uint32_t>(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<std::chrono::milliseconds>(now - m_ChronoLastPingTime);
m_AccumulatedPingTime += duration;
m_ChronoLastPongTime = now;
}
void TcpSession::DoPing() {
if (!m_IsConnected) {
return;
}
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(ID_INTERNAL_PING);
bitStream.Write<uint32_t>(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<float>(m_AccumulatedPingTime.count()) / m_PingCount;
}
float TcpSession::GetLastPing() const {
return static_cast<float>(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);
}

80
dNet/TcpSession.h Normal file
View File

@ -0,0 +1,80 @@
#pragma once
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <thread>
#include <mutex>
#include <queue>
#include <memory>
#include <chrono>
#include "RakNetTypes.h"
#include "eServerDisconnectIdentifiers.h"
#include "BitStream.h"
class TcpTransportLayer;
class TcpSession : public std::enable_shared_from_this<TcpSession> {
public:
TcpSession(boost::asio::io_service& io_service, boost::asio::ssl::context* sslContext, TcpTransportLayer* server);
void Start();
void Send(const std::vector<uint8_t>& data);
void Send(const RakNet::BitStream& data);
boost::asio::basic_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>& 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<boost::asio::ip::tcp::socket> m_Socket;
std::unique_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_SSLStream;
bool m_UseTls;
TcpTransportLayer* m_Server;
SystemAddress m_SystemAddress;
bool m_IsConnected;
bool m_ReadyForUserPackets;
std::queue<Packet*> m_UserPacketQueue;
std::vector<uint8_t> m_HeaderBuffer;
std::vector<uint8_t> 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<std::chrono::high_resolution_clock> m_ChronoLastPingTime;
std::chrono::time_point<std::chrono::high_resolution_clock> m_ChronoLastPongTime;
// Accumulated ping time
std::chrono::milliseconds m_AccumulatedPingTime;
};

426
dNet/TcpTransportLayer.cpp Normal file
View File

@ -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<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) {
switch (static_cast<eManagerMessageType>(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<std::mutex> 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>(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<boost::asio::ip::tcp::acceptor>(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<SystemAddress> 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<std::mutex> 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<char*>(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<uint8_t>(ID_CONNECTION_REQUEST_ACCEPTED);
bitStream.Write<uint32_t>(packet->systemAddress.binaryAddress);
bitStream.Write<uint16_t>(packet->systemAddress.port);
bitStream.Write<uint16_t>(0);
// Our ip and port
bitStream.Write<uint32_t>(inet_addr(m_IP.c_str()));
bitStream.Write<uint16_t>(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<TcpSession>(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<TcpSession>& 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<int32_t>(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;
}

158
dNet/TcpTransportLayer.h Normal file
View File

@ -0,0 +1,158 @@
#pragma once
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <thread>
#include <mutex>
#include <queue>
#include <memory>
#include <csignal>
#include <unordered_map>
#include <functional>
#include <boost/functional/hash.hpp>
#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<SystemAddress> {
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<TcpSession>& session, const boost::system::error_code& ec);
boost::asio::io_service m_IOService;
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_Acceptor;
std::unique_ptr<boost::asio::ssl::context> m_SSLContext;
bool m_UseTls;
bool m_VerifyCertificate;
std::unordered_map<SystemAddress, std::shared_ptr<TcpSession>> m_ClientSessions;
std::queue<Packet*> 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;
};

193
dNet/TransportLayer.h Normal file
View File

@ -0,0 +1,193 @@
#pragma once
#include <string>
#include <csignal>
#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;
};

View File

@ -0,0 +1,25 @@
#pragma once
#include <string>
#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;
};

View File

@ -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);
}

View File

@ -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<RakNetTransportLayer>(
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<TcpTransportLayer>(
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<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) {
switch (static_cast<eMasterMessageType>(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<TransportLayer>& dServer::GetTransportLayer() const {
return m_TransportLayer;
}
const TransportType dServer::GetTransportType() const {
return m_TransportType;
}

View File

@ -1,33 +1,15 @@
#pragma once
#include <string>
#include <csignal>
#include "RakPeerInterface.h"
#include "ReplicaManager.h"
#include "NetworkIDManager.h"
#include <memory>
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<TransportLayer>& 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<TransportLayer> m_TransportLayer;
};

View File

@ -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<uint16_t>(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<eConnectionType>(packet->data[1]) != eConnectionType::MASTER || packet->length < 4) return;
switch (static_cast<eMasterMessageType>(packet->data[3])) {
case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: {
switch (static_cast<eManagerMessageType>(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<LWOMAPID>(Game::server->GetZoneID());
bitStream.Write<LWOINSTANCEID>(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<LWOMAPID>(Game::server->GetZoneID());
bitStream.Write<LWOINSTANCEID>(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<SkillComponent>();
@ -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);
}

View File

@ -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;