mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
Adds CommonPackets (ServiceType::COMMON): ClientVersionConfirm (the client's handshake, with the handler that logs it and answers), ServerVersionConfirm, DisconnectNotify and GeneralNotify (layout from the 1.10.64 client's PacketHandler_MSG_SERVER_GENERAL_NOTIFY; DLU does not send it yet). AuthPackets::LoginRequest reads the login and keeps the old login logic in its Handle; ClientPackets::LoginResponse (with the Stamp list) replaces the hand written response, and AuthPackets::SendLoginResponse fills it from the settings as before. dServer::Disconnect writes a DisconnectNotify. AuthServer's if-chain and WorldServer's COMMON case become CommonPackets::Handle / AuthPackets::Handle, dispatch maps that read the struct, drop and log it if it does not deserialize, then Handle. HandleHandshake and SendHandshake are gone. No wire change. Verified against a frozen verbatim copy of the old functions (tests/dGameTests/dNetTests/Legacy): the old handshake and login handlers and the new dispatchers are fed the same packets and must send identical bytes to the same address (same RNG seed for the session key), over grids of versions, service types, ports, response codes, error messages, IPs and stamp lists; plus hand computed golden bytes, round trips, truncation checks, and a deliberate field width mutation that made the tests fail. The only behaviour difference: truncated handshake and login packets are now dropped instead of handled with whatever was read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
296 lines
8.9 KiB
C++
296 lines
8.9 KiB
C++
#define _VARIADIC_MAX 10
|
|
#include "dServer.h"
|
|
#include "dNetCommon.h"
|
|
#include "Logger.h"
|
|
#include "dConfig.h"
|
|
|
|
#include "RakNetworkFactory.h"
|
|
#include "MessageIdentifiers.h"
|
|
#include "ServiceType.h"
|
|
#include "MessageType/Server.h"
|
|
#include "MessageType/Master.h"
|
|
|
|
#include "BinaryPathFinder.h"
|
|
#include "BitStreamUtils.h"
|
|
#include "CommonPackets.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;
|
|
}
|
|
} 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,
|
|
ServiceType serverType,
|
|
dConfig* config,
|
|
Game::signal_t* lastSignal,
|
|
const std::string& masterPassword,
|
|
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;
|
|
mMasterPassword = masterPassword;
|
|
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);
|
|
#ifdef DARKFLAME_PLATFORM_LINUX
|
|
if (mServerType == ServiceType::AUTH) {
|
|
const auto cwd = BinaryPathFinder::GetBinaryDir();
|
|
LOG("Try running the following command before launching again:\n sudo setcap 'cap_net_bind_service=+ep' \"%s/AuthServer\"", cwd.string().c_str());
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
mLogger->SetLogToConsole(prevLogSetting);
|
|
|
|
//Connect to master if we are not master:
|
|
if (serverType != ServiceType::MASTER) {
|
|
SetupForMasterConnection();
|
|
if (!ConnectToMaster()) {
|
|
LOG("Failed ConnectToMaster!");
|
|
}
|
|
}
|
|
|
|
//Set up Replica if we're a world server:
|
|
if (serverType == ServiceType::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);
|
|
}
|
|
}
|
|
|
|
dServer::~dServer() {
|
|
Shutdown();
|
|
}
|
|
|
|
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; }
|
|
|
|
switch (packet->data[0]) {
|
|
case ID_DISCONNECTION_NOTIFICATION:
|
|
[[fallthrough]];
|
|
case ID_CONNECTION_LOST: {
|
|
LOG("Lost our connection to master, shutting DOWN!");
|
|
mMasterConnectionActive = false;
|
|
// ConnectToMaster(); // We'll just shut down now
|
|
break;
|
|
}
|
|
case ID_CONNECTION_REQUEST_ACCEPTED: {
|
|
LOG("Established connection to master: ServiceType (%s), Zone (%i), Instance (%i)", StringifiedEnum::ToString(this->GetServerType()).data(), this->GetZoneID(), this->GetInstanceID());
|
|
mMasterConnectionActive = true;
|
|
mMasterSystemAddress = packet->systemAddress;
|
|
MasterPackets::SendServerInfo(this, packet);
|
|
break;
|
|
}
|
|
case ID_USER_PACKET_ENUM: {
|
|
if (static_cast<ServiceType>(packet->data[1]) == ServiceType::MASTER) {
|
|
switch (static_cast<MessageType::Master>(packet->data[3])) {
|
|
case MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE: {
|
|
ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(packet);
|
|
break;
|
|
}
|
|
case MessageType::Master::SHUTDOWN:
|
|
*mShouldShutdown = -2;
|
|
break;
|
|
|
|
case MessageType::Master::CONFIG_RELOAD:
|
|
LOG("Reloading settings (changed on the dashboard)");
|
|
if (mConfig) mConfig->ReloadConfig();
|
|
break;
|
|
|
|
// When we handle these packets in World instead dServer, we just return the packet's pointer.
|
|
default:
|
|
return packet;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
mMasterPeer->DeallocatePacket(packet);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Packet* dServer::Receive() {
|
|
return mPeer->Receive();
|
|
}
|
|
|
|
void dServer::DeallocatePacket(Packet* packet) {
|
|
mPeer->DeallocatePacket(packet);
|
|
}
|
|
|
|
void dServer::DeallocateMasterPacket(Packet* packet) {
|
|
mMasterPeer->DeallocatePacket(packet);
|
|
}
|
|
|
|
void dServer::Send(RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast) {
|
|
if (mSendObserver) mSendObserver(bitStream, sysAddr, broadcast);
|
|
mPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, sysAddr, broadcast);
|
|
}
|
|
|
|
void dServer::SendToMaster(RakNet::BitStream& bitStream) {
|
|
if (!mMasterConnectionActive) ConnectToMaster();
|
|
mMasterPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, mMasterSystemAddress, false);
|
|
}
|
|
|
|
void dServer::Disconnect(const SystemAddress& sysAddr, eServerDisconnectIdentifiers disconNotifyID) {
|
|
CommonPackets::DisconnectNotify notify;
|
|
notify.disconnectID = disconNotifyID;
|
|
RakNet::BitStream bitStream;
|
|
notify.WritePacket(bitStream);
|
|
mPeer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, sysAddr, false);
|
|
|
|
mPeer->CloseConnection(sysAddr, true);
|
|
}
|
|
|
|
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 (mUseEncryption) mPeer->InitializeSecurity(nullptr, nullptr, nullptr, nullptr);
|
|
if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false;
|
|
|
|
if (mIsInternal) {
|
|
mPeer->SetIncomingPassword(mMasterPassword.c_str(), mMasterPassword.size());
|
|
} else {
|
|
UpdateBandwidthLimit();
|
|
UpdateMaximumMtuSize();
|
|
mPeer->SetIncomingPassword(NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
|
|
}
|
|
|
|
mPeer->SetMaximumIncomingConnections(mMaxConnections);
|
|
|
|
return true;
|
|
}
|
|
|
|
void dServer::UpdateMaximumMtuSize() {
|
|
mPeer->SetMTUSize(mConfig->GetValue<int32_t>("maximum_mtu_size", 1228));
|
|
}
|
|
|
|
void dServer::UpdateBandwidthLimit() {
|
|
mPeer->SetPerConnectionOutgoingBandwidthLimit(mConfig->GetValue<int32_t>("maximum_outgoing_bandwidth", 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 != ServiceType::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, mMasterPassword.c_str(), mMasterPassword.size());
|
|
}
|
|
|
|
void dServer::UpdateReplica() {
|
|
mReplicaManager->Update(mPeer);
|
|
}
|
|
|
|
int dServer::GetPing(const SystemAddress& sysAddr) const {
|
|
return mPeer->GetAveragePing(sysAddr);
|
|
}
|
|
|
|
int dServer::GetLatestPing(const SystemAddress& sysAddr) const {
|
|
return mPeer->GetLastPing(sysAddr);
|
|
}
|