mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-01-08 12:52:36 +00:00
Public release of the DLU server code!
Have fun!
This commit is contained in:
397
dMasterServer/InstanceManager.cpp
Normal file
397
dMasterServer/InstanceManager.cpp
Normal file
@@ -0,0 +1,397 @@
|
||||
#include "InstanceManager.h"
|
||||
#include <string>
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
#include "dLogger.h"
|
||||
#include "dConfig.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
#include "dMessageIdentifiers.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "PacketUtils.h"
|
||||
|
||||
InstanceManager::InstanceManager(dLogger* logger, const std::string& externalIP) {
|
||||
mLogger = logger;
|
||||
mExternalIP = externalIP;
|
||||
m_LastPort = std::atoi(Game::config->GetValue("world_port_start").c_str());
|
||||
m_LastInstanceID = LWOINSTANCEID_INVALID;
|
||||
}
|
||||
|
||||
InstanceManager::~InstanceManager() {
|
||||
for (Instance* i : m_Instances) {
|
||||
delete i;
|
||||
i = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Instance * InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneID) {
|
||||
mLogger->Log("InstanceManager", "Searching for an instance for mapID %i/%i\n", mapID, cloneID);
|
||||
Instance* instance = FindInstance(mapID, isFriendTransfer, cloneID);
|
||||
if (instance) return instance;
|
||||
|
||||
//TODO: Update this so that the IP is read from a configuration file instead
|
||||
|
||||
int softCap = 8;
|
||||
int maxPlayers = 12;
|
||||
|
||||
if (mapID == 0) {
|
||||
softCap = 999;
|
||||
maxPlayers = softCap;
|
||||
} else {
|
||||
softCap = GetSoftCap(mapID);
|
||||
maxPlayers = GetHardCap(mapID);
|
||||
}
|
||||
|
||||
instance = new Instance(mExternalIP, ++m_LastPort, mapID, ++m_LastInstanceID, cloneID, softCap, maxPlayers);
|
||||
|
||||
//Start the actual process:
|
||||
#ifdef _WIN32
|
||||
std::string cmd = "start ./WorldServer.exe -zone ";
|
||||
#else
|
||||
std::string cmd;
|
||||
if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) {
|
||||
cmd = "sudo ./WorldServer -zone ";
|
||||
} else {
|
||||
cmd = "./WorldServer -zone ";
|
||||
}
|
||||
#endif
|
||||
|
||||
cmd.append(std::to_string(mapID));
|
||||
cmd.append(" -port ");
|
||||
cmd.append(std::to_string(m_LastPort));
|
||||
cmd.append(" -instance ");
|
||||
cmd.append(std::to_string(m_LastInstanceID));
|
||||
cmd.append(" -maxclients ");
|
||||
cmd.append(std::to_string(maxPlayers));
|
||||
|
||||
cmd.append(" -clone ");
|
||||
cmd.append(std::to_string(cloneID));
|
||||
|
||||
#ifndef _WIN32
|
||||
cmd.append("&"); //Sends our next process to the background on Linux
|
||||
#endif
|
||||
|
||||
system(cmd.c_str());
|
||||
|
||||
m_LastPort++; //Increment it again because the next port is for World<->Server comm.
|
||||
m_LastPort++; //Increment it again because the next port is for World<->Chat comm.
|
||||
m_Instances.push_back(instance);
|
||||
|
||||
if (instance) {
|
||||
mLogger->Log("InstanceManager", "Created new instance: %i/%i/%i with min/max %i/%i\n", mapID, m_LastInstanceID, cloneID, softCap, maxPlayers);
|
||||
return instance;
|
||||
}
|
||||
else mLogger->Log("InstanceManager", "Failed to create a new instance!\n");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool InstanceManager::IsPortInUse(uint32_t port) {
|
||||
for (Instance* i : m_Instances) {
|
||||
if (i && i->GetPort() == port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InstanceManager::AddPlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
Instance* inst = FindInstance(mapID, instanceID);
|
||||
if (inst) {
|
||||
Player player;
|
||||
player.addr = systemAddr;
|
||||
player.id = 0; //TODO: Update this to include the LWOOBJID of the player's character.
|
||||
inst->AddPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceManager::RemovePlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
Instance* inst = FindInstance(mapID, instanceID);
|
||||
if (inst) {
|
||||
Player player;
|
||||
player.addr = systemAddr;
|
||||
player.id = 0; //TODO: Update this to include the LWOOBJID of the player's character.
|
||||
inst->RemovePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Instance*> InstanceManager::GetInstances() const
|
||||
{
|
||||
return m_Instances;
|
||||
}
|
||||
|
||||
void InstanceManager::AddInstance(Instance* instance) {
|
||||
if (instance == nullptr) return;
|
||||
|
||||
m_Instances.push_back(instance);
|
||||
}
|
||||
|
||||
void InstanceManager::RemoveInstance(Instance* instance) {
|
||||
for (uint32_t i = 0; i < m_Instances.size(); ++i)
|
||||
{
|
||||
if (m_Instances[i] == instance)
|
||||
{
|
||||
instance->SetShutdownComplete(true);
|
||||
|
||||
RedirectPendingRequests(instance);
|
||||
|
||||
delete m_Instances[i];
|
||||
|
||||
m_Instances.erase(m_Instances.begin() + i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceManager::ReadyInstance(Instance* instance)
|
||||
{
|
||||
instance->SetIsReady(true);
|
||||
|
||||
auto& pending = instance->GetPendingRequests();
|
||||
|
||||
for (const auto& request : pending)
|
||||
{
|
||||
const auto& zoneId = instance->GetZoneID();
|
||||
|
||||
Game::logger->Log("InstanceManager", "Responding to pending request %llu -> %i (%i)\n", request, zoneId.GetMapID(), zoneId.GetCloneID());
|
||||
|
||||
MasterPackets::SendZoneTransferResponse(
|
||||
Game::server,
|
||||
request.sysAddr,
|
||||
request.id,
|
||||
request.mythranShift,
|
||||
zoneId.GetMapID(),
|
||||
zoneId.GetInstanceID(),
|
||||
zoneId.GetCloneID(),
|
||||
instance->GetIP(),
|
||||
instance->GetPort()
|
||||
);
|
||||
}
|
||||
|
||||
pending.clear();
|
||||
}
|
||||
|
||||
void InstanceManager::RequestAffirmation(Instance* instance, const PendingInstanceRequest& request)
|
||||
{
|
||||
instance->GetPendingAffirmations().push_back(request);
|
||||
|
||||
CBITSTREAM;
|
||||
|
||||
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_AFFIRM_TRANSFER_REQUEST);
|
||||
|
||||
bitStream.Write(request.id);
|
||||
|
||||
Game::server->Send(&bitStream, instance->GetSysAddr(), false);
|
||||
|
||||
Game::logger->Log("MasterServer", "Sent affirmation request %llu to %i/%i\n", request.id,
|
||||
static_cast<int>(instance->GetZoneID().GetMapID()),
|
||||
static_cast<int>(instance->GetZoneID().GetCloneID())
|
||||
);
|
||||
}
|
||||
|
||||
void InstanceManager::AffirmTransfer(Instance* instance, const uint64_t transferID)
|
||||
{
|
||||
auto& pending = instance->GetPendingAffirmations();
|
||||
|
||||
for (auto i = 0u; i < pending.size(); ++i)
|
||||
{
|
||||
const auto& request = pending[i];
|
||||
|
||||
if (request.id != transferID) continue;
|
||||
|
||||
const auto& zoneId = instance->GetZoneID();
|
||||
|
||||
MasterPackets::SendZoneTransferResponse(
|
||||
Game::server,
|
||||
request.sysAddr,
|
||||
request.id,
|
||||
request.mythranShift,
|
||||
zoneId.GetMapID(),
|
||||
zoneId.GetInstanceID(),
|
||||
zoneId.GetCloneID(),
|
||||
instance->GetIP(),
|
||||
instance->GetPort()
|
||||
);
|
||||
|
||||
pending.erase(pending.begin() + i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceManager::RedirectPendingRequests(Instance* instance)
|
||||
{
|
||||
const auto& zoneId = instance->GetZoneID();
|
||||
|
||||
for (const auto& request : instance->GetPendingAffirmations())
|
||||
{
|
||||
auto* in = Game::im->GetInstance(zoneId.GetMapID(), false, zoneId.GetCloneID());
|
||||
|
||||
if (!in->GetIsReady()) // Instance not ready, make a pending request
|
||||
{
|
||||
in->GetPendingRequests().push_back(request);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Game::im->RequestAffirmation(in, request);
|
||||
}
|
||||
}
|
||||
|
||||
Instance* InstanceManager::GetInstanceBySysAddr(SystemAddress& sysAddr) {
|
||||
for (uint32_t i = 0; i < m_Instances.size(); ++i) {
|
||||
if (m_Instances[i] && m_Instances[i]->GetSysAddr() == sysAddr) {
|
||||
return m_Instances[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool InstanceManager::IsInstanceFull(Instance* instance, bool isFriendTransfer) {
|
||||
if (!isFriendTransfer && instance->GetSoftCap() > instance->GetCurrentClientCount())
|
||||
return false;
|
||||
else if (isFriendTransfer && instance->GetHardCap() > instance->GetCurrentClientCount())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Instance * InstanceManager::FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId) {
|
||||
for (Instance* i : m_Instances) {
|
||||
if (i && i->GetMapID() == mapID && i->GetCloneID() == cloneId && !IsInstanceFull(i, isFriendTransfer) && !i->GetIsPrivate() && !i->GetShutdownComplete()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Instance * InstanceManager::FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
for (Instance* i : m_Instances) {
|
||||
if (i && i->GetMapID() == mapID && i->GetInstanceID() == instanceID && !i->GetIsPrivate()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password)
|
||||
{
|
||||
auto* instance = FindPrivateInstance(password);
|
||||
|
||||
if (instance != nullptr)
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
int maxPlayers = 999;
|
||||
|
||||
instance = new Instance(mExternalIP, ++m_LastPort, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
|
||||
|
||||
//Start the actual process:
|
||||
std::string cmd = "start ./WorldServer.exe -zone ";
|
||||
|
||||
#ifndef _WIN32
|
||||
cmd = "./WorldServer -zone ";
|
||||
#endif
|
||||
|
||||
cmd.append(std::to_string(mapID));
|
||||
cmd.append(" -port ");
|
||||
cmd.append(std::to_string(m_LastPort));
|
||||
cmd.append(" -instance ");
|
||||
cmd.append(std::to_string(m_LastInstanceID));
|
||||
cmd.append(" -maxclients ");
|
||||
cmd.append(std::to_string(maxPlayers));
|
||||
|
||||
cmd.append(" -clone ");
|
||||
cmd.append(std::to_string(cloneID));
|
||||
|
||||
#ifndef WIN32
|
||||
cmd.append("&"); //Sends our next process to the background on Linux
|
||||
#endif
|
||||
|
||||
system(cmd.c_str());
|
||||
|
||||
m_LastPort++; //Increment it again because the next port is for World<->Server comm.
|
||||
m_LastPort++; //Increment it again because the next port is for World<->Chat comm.
|
||||
m_Instances.push_back(instance);
|
||||
|
||||
if (instance) return instance;
|
||||
else mLogger->Log("InstanceManager", "Failed to create a new instance!\n");
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
Instance* InstanceManager::FindPrivateInstance(const std::string& password)
|
||||
{
|
||||
for (auto* instance : m_Instances)
|
||||
{
|
||||
if (!instance) continue;
|
||||
|
||||
if (!instance->GetIsPrivate())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
mLogger->Log("InstanceManager", "Password: %s == %s => %d\n", password.c_str(), instance->GetPassword().c_str(), password == instance->GetPassword());
|
||||
|
||||
if (instance->GetPassword() == password)
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int InstanceManager::GetSoftCap(LWOMAPID mapID) {
|
||||
CDZoneTableTable* zoneTable = CDClientManager::Instance()->GetTable<CDZoneTableTable>("ZoneTable");
|
||||
if (zoneTable) {
|
||||
const CDZoneTable* zone = zoneTable->Query(mapID);
|
||||
|
||||
if (zone != nullptr) {
|
||||
return zone->population_soft_cap;
|
||||
}
|
||||
}
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
int InstanceManager::GetHardCap(LWOMAPID mapID) {
|
||||
CDZoneTableTable* zoneTable = CDClientManager::Instance()->GetTable<CDZoneTableTable>("ZoneTable");
|
||||
if (zoneTable) {
|
||||
const CDZoneTable* zone = zoneTable->Query(mapID);
|
||||
|
||||
if (zone != nullptr) {
|
||||
return zone->population_hard_cap;
|
||||
}
|
||||
}
|
||||
|
||||
return 12;
|
||||
}
|
||||
|
||||
void Instance::SetShutdownComplete(const bool value)
|
||||
{
|
||||
m_Shutdown = value;
|
||||
}
|
||||
|
||||
bool Instance::GetShutdownComplete() const
|
||||
{
|
||||
return m_Shutdown;
|
||||
}
|
||||
|
||||
void Instance::Shutdown()
|
||||
{
|
||||
CBITSTREAM;
|
||||
|
||||
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_SHUTDOWN);
|
||||
|
||||
Game::server->Send(&bitStream, this->m_SysAddr, false);
|
||||
|
||||
Game::logger->Log("Instance", "Triggered world shutdown\n");
|
||||
}
|
||||
138
dMasterServer/InstanceManager.h
Normal file
138
dMasterServer/InstanceManager.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "dCommonVars.h"
|
||||
#include "RakNetTypes.h"
|
||||
#include "dZMCommon.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
struct Player {
|
||||
LWOOBJID id;
|
||||
SystemAddress addr;
|
||||
};
|
||||
|
||||
struct PendingInstanceRequest {
|
||||
uint64_t id;
|
||||
bool mythranShift;
|
||||
SystemAddress sysAddr;
|
||||
};
|
||||
|
||||
class Instance {
|
||||
public:
|
||||
Instance(const std::string& ip, uint32_t port, LWOMAPID mapID, LWOINSTANCEID instanceID, LWOCLONEID cloneID, int softCap, int hardCap, bool isPrivate = false, std::string password = "") {
|
||||
m_IP = ip;
|
||||
m_Port = port;
|
||||
m_ZoneID = LWOZONEID(mapID, instanceID, cloneID);
|
||||
m_MaxClientsSoftCap = softCap;
|
||||
m_MaxClientsHardCap = hardCap;
|
||||
m_CurrentClientCount = 0;
|
||||
m_IsPrivate = isPrivate;
|
||||
m_Password = password;
|
||||
m_Shutdown = false; //by default we are not shutting down
|
||||
m_PendingAffirmations = {};
|
||||
m_PendingRequests = {};
|
||||
m_Ready = false;
|
||||
}
|
||||
|
||||
const std::string& GetIP() const { return m_IP; }
|
||||
uint32_t GetPort() const { return m_Port; }
|
||||
const LWOZONEID& GetZoneID() const { return m_ZoneID; }
|
||||
|
||||
LWOMAPID GetMapID() const { return m_ZoneID.GetMapID(); }
|
||||
LWOINSTANCEID GetInstanceID() const { return m_ZoneID.GetInstanceID(); }
|
||||
LWOCLONEID GetCloneID() const { return m_ZoneID.GetCloneID(); }
|
||||
|
||||
bool GetIsPrivate() const { return m_IsPrivate; }
|
||||
const std::string& GetPassword() const { return m_Password; }
|
||||
|
||||
bool GetIsReady() const { return m_Ready; }
|
||||
void SetIsReady(bool value) { m_Ready = value; }
|
||||
std::vector<PendingInstanceRequest>& GetPendingRequests() { return m_PendingRequests; }
|
||||
std::vector<PendingInstanceRequest>& GetPendingAffirmations() { return m_PendingAffirmations; }
|
||||
|
||||
int GetHardCap() const { return m_MaxClientsHardCap; }
|
||||
int GetSoftCap() const { return m_MaxClientsSoftCap; }
|
||||
int GetCurrentClientCount() const { return m_CurrentClientCount; }
|
||||
|
||||
void SetAffirmationTimeout(const uint32_t value) { m_AffirmationTimeout = value; }
|
||||
uint32_t GetAffirmationTimeout() const { return m_AffirmationTimeout; }
|
||||
|
||||
void AddPlayer(Player player) { /*m_Players.push_back(player);*/ m_CurrentClientCount++; }
|
||||
void RemovePlayer(Player player) {
|
||||
m_CurrentClientCount--;
|
||||
if (m_CurrentClientCount < 0) m_CurrentClientCount = 0;
|
||||
/*for (size_t i = 0; i < m_Players.size(); ++i)
|
||||
if (m_Players[i].addr == player.addr) m_Players.erase(m_Players.begin() + i);*/
|
||||
}
|
||||
|
||||
void SetSysAddr(SystemAddress sysAddr) { m_SysAddr = sysAddr; }
|
||||
const SystemAddress& GetSysAddr() const { return m_SysAddr; }
|
||||
|
||||
void SetShutdownComplete(bool value);
|
||||
bool GetShutdownComplete() const;
|
||||
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
std::string m_IP;
|
||||
uint32_t m_Port;
|
||||
LWOZONEID m_ZoneID;
|
||||
int m_MaxClientsSoftCap;
|
||||
int m_MaxClientsHardCap;
|
||||
int m_CurrentClientCount;
|
||||
std::vector<Player> m_Players;
|
||||
SystemAddress m_SysAddr;
|
||||
bool m_Ready;
|
||||
std::vector<PendingInstanceRequest> m_PendingRequests;
|
||||
std::vector<PendingInstanceRequest> m_PendingAffirmations;
|
||||
|
||||
uint32_t m_AffirmationTimeout;
|
||||
|
||||
bool m_IsPrivate;
|
||||
std::string m_Password;
|
||||
|
||||
bool m_Shutdown;
|
||||
|
||||
//Private functions:
|
||||
};
|
||||
|
||||
class InstanceManager {
|
||||
public:
|
||||
InstanceManager(dLogger* logger, const std::string& externalIP);
|
||||
~InstanceManager();
|
||||
|
||||
Instance* GetInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneID); //Creates an instance if none found
|
||||
bool IsPortInUse(uint32_t port);
|
||||
|
||||
void AddPlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
void RemovePlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
|
||||
std::vector<Instance*> GetInstances() const;
|
||||
void AddInstance(Instance* instance);
|
||||
void RemoveInstance(Instance* instance);
|
||||
|
||||
void ReadyInstance(Instance* instance);
|
||||
void RequestAffirmation(Instance* instance, const PendingInstanceRequest& request);
|
||||
void AffirmTransfer(Instance* instance, uint64_t transferID);
|
||||
|
||||
void RedirectPendingRequests(Instance* instance);
|
||||
|
||||
Instance* GetInstanceBySysAddr(SystemAddress& sysAddr);
|
||||
|
||||
Instance* FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId = 0);
|
||||
Instance* FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
|
||||
Instance* CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password);
|
||||
Instance* FindPrivateInstance(const std::string& password);
|
||||
|
||||
private:
|
||||
dLogger* mLogger;
|
||||
std::string mExternalIP;
|
||||
std::vector<Instance*> m_Instances;
|
||||
unsigned short m_LastPort;
|
||||
LWOINSTANCEID m_LastInstanceID;
|
||||
|
||||
//Private functions:
|
||||
bool IsInstanceFull(Instance* instance, bool isFriendTransfer);
|
||||
int GetSoftCap(LWOMAPID mapID);
|
||||
int GetHardCap(LWOMAPID mapID);
|
||||
};
|
||||
719
dMasterServer/MasterServer.cpp
Normal file
719
dMasterServer/MasterServer.cpp
Normal file
@@ -0,0 +1,719 @@
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <bcrypt/BCrypt.hpp>
|
||||
#else
|
||||
#include <bcrypt.h>
|
||||
#endif
|
||||
|
||||
#include <csignal>
|
||||
|
||||
//DLU Includes:
|
||||
#include "CDClientDatabase.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "Database.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "dConfig.h"
|
||||
#include "dLogger.h"
|
||||
#include "dServer.h"
|
||||
|
||||
//RakNet includes:
|
||||
#include "RakNetDefines.h"
|
||||
|
||||
//Packet includes:
|
||||
|
||||
#include "AuthPackets.h"
|
||||
#include "Game.h"
|
||||
#include "InstanceManager.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "dMessageIdentifiers.h"
|
||||
|
||||
namespace Game {
|
||||
dLogger* logger;
|
||||
dServer* server;
|
||||
InstanceManager* im;
|
||||
dConfig* config;
|
||||
} //namespace Game
|
||||
|
||||
bool shutdownSequenceStarted = false;
|
||||
void ShutdownSequence();
|
||||
dLogger* SetupLogger();
|
||||
void HandlePacket(Packet* packet);
|
||||
std::map<uint32_t, std::string> activeSessions;
|
||||
bool shouldShutdown = false;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Diagnostics::SetProcessName("Master");
|
||||
Diagnostics::SetProcessFileName(argv[0]);
|
||||
Diagnostics::Initialize();
|
||||
|
||||
//Triggers the shutdown sequence at application exit
|
||||
std::atexit(ShutdownSequence);
|
||||
signal(SIGINT, [](int) { ShutdownSequence(); });
|
||||
|
||||
//Create all the objects we need to run our service:
|
||||
Game::logger = SetupLogger();
|
||||
if (!Game::logger) return 0;
|
||||
|
||||
Game::logger->Log("MasterServer", "Starting Master server...\n");
|
||||
Game::logger->Log("MasterServer", "Version: %i.%i\n", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||
Game::logger->Log("MasterServer", "Compiled on: %s\n", __TIMESTAMP__);
|
||||
|
||||
//Read our config:
|
||||
dConfig config("masterconfig.ini");
|
||||
Game::config = &config;
|
||||
Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console"))));
|
||||
|
||||
//Connect to CDClient
|
||||
try {
|
||||
CDClientDatabase::Connect("./res/CDServer.sqlite");
|
||||
} catch (CppSQLite3Exception& e) {
|
||||
Game::logger->Log("WorldServer", "Unable to connect to CDServer SQLite Database\n");
|
||||
Game::logger->Log("WorldServer", "Error: %s\n", e.errorMessage());
|
||||
Game::logger->Log("WorldServer", "Error Code: %i\n", e.errorCode());
|
||||
return -1;
|
||||
}
|
||||
|
||||
CDClientManager::Instance()->Initialize();
|
||||
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = config.GetValue("mysql_host");
|
||||
std::string mysql_database = config.GetValue("mysql_database");
|
||||
std::string mysql_username = config.GetValue("mysql_username");
|
||||
std::string mysql_password = config.GetValue("mysql_password");
|
||||
|
||||
try {
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("MasterServer", "Got an error while connecting to the database: %s\n", ex.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
//If the first command line argument is -a or --account then make the user
|
||||
//input a username and password, with the password being hidden.
|
||||
if (argc > 1 &&
|
||||
(strcmp(argv[1], "-a") == 0 || strcmp(argv[1], "--account") == 0)) {
|
||||
std::string username;
|
||||
std::string password;
|
||||
|
||||
std::cout << "Enter a username: ";
|
||||
std::cin >> username;
|
||||
|
||||
//Read the password from the console without echoing it.
|
||||
#ifdef __linux__
|
||||
//This function is obsolete, but it only meant to be used by the
|
||||
//sysadmin to create their first account.
|
||||
password = getpass("Enter a password: ");
|
||||
#else
|
||||
std::cout << "Enter a password: ";
|
||||
std::cin >> password;
|
||||
#endif
|
||||
|
||||
//Generate new hash for bcrypt
|
||||
|
||||
char salt[BCRYPT_HASHSIZE];
|
||||
char hash[BCRYPT_HASHSIZE];
|
||||
|
||||
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
|
||||
|
||||
assert(bcryptState == 0);
|
||||
|
||||
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
|
||||
|
||||
assert(bcryptState == 0);
|
||||
|
||||
//Create account
|
||||
|
||||
auto* statement = Database::CreatePreppedStmt("INSERT INTO accounts (name, password, ""gm_level) VALUES (?, ?, ?);");
|
||||
statement->setString(1, username);
|
||||
statement->setString(2, std::string(hash, BCRYPT_HASHSIZE).c_str());
|
||||
statement->setInt(3, 9);
|
||||
|
||||
statement->execute();
|
||||
|
||||
delete statement;
|
||||
|
||||
std::cout << "Account created successfully!\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
int maxClients = 999;
|
||||
int ourPort = 1000;
|
||||
if (config.GetValue("max_clients") != "") maxClients = std::stoi(config.GetValue("max_clients"));
|
||||
if (config.GetValue("port") != "") ourPort = std::stoi(config.GetValue("port"));
|
||||
|
||||
Game::server = new dServer(config.GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master);
|
||||
|
||||
//Query for the database for a server labeled "master"
|
||||
auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'");
|
||||
auto* result = masterLookupStatement->executeQuery();
|
||||
|
||||
//If we found a server, update it's IP and port to the current one.
|
||||
if (result->next()) {
|
||||
auto* updateStatement = Database::CreatePreppedStmt("UPDATE `servers` SET `ip` = ?, `port` = ? WHERE `id` = ?");
|
||||
updateStatement->setString(1, Game::server->GetIP());
|
||||
updateStatement->setInt(2, Game::server->GetPort());
|
||||
updateStatement->setInt(3, result->getInt("id"));
|
||||
updateStatement->execute();
|
||||
delete updateStatement;
|
||||
}
|
||||
else {
|
||||
//If we didn't find a server, create one.
|
||||
auto* insertStatement = Database::CreatePreppedStmt("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171023)");
|
||||
insertStatement->setString(1, Game::server->GetIP());
|
||||
insertStatement->setInt(2, Game::server->GetPort());
|
||||
insertStatement->execute();
|
||||
delete insertStatement;
|
||||
}
|
||||
|
||||
//Create additional objects here:
|
||||
ObjectIDManager::Instance()->Initialize(Game::logger);
|
||||
Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
|
||||
|
||||
//Depending on the config, start up servers:
|
||||
if (config.GetValue("prestart_servers") != "" && config.GetValue("prestart_servers") == "1") {
|
||||
#ifdef __APPLE__
|
||||
//macOS doesn't need sudo to run on ports < 1024
|
||||
system("./ChatServer&");
|
||||
#elif _WIN32
|
||||
system("start ./ChatServer.exe");
|
||||
#else
|
||||
if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
|
||||
system("sudo ./ChatServer&");
|
||||
}
|
||||
else {
|
||||
system("./ChatServer&");
|
||||
}
|
||||
#endif
|
||||
|
||||
Game::im->GetInstance(0, false, 0)->SetIsReady(true);
|
||||
Game::im->GetInstance(1000, false, 0)->SetIsReady(true);
|
||||
|
||||
#ifdef __APPLE__
|
||||
system("./AuthServer&");
|
||||
#elif _WIN32
|
||||
system("start ./AuthServer.exe");
|
||||
#else
|
||||
if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
|
||||
system("sudo ./AuthServer&");
|
||||
}
|
||||
else {
|
||||
system("./AuthServer&");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
Packet* packet = nullptr;
|
||||
int framesSinceLastFlush = 0;
|
||||
int framesSinceLastSQLPing = 0;
|
||||
int framesSinceKillUniverseCommand = 0;
|
||||
|
||||
while (true) {
|
||||
//In world we'd update our other systems here.
|
||||
|
||||
//Check for packets here:
|
||||
packet = Game::server->Receive();
|
||||
if (packet) {
|
||||
HandlePacket(packet);
|
||||
Game::server->DeallocatePacket(packet);
|
||||
packet = nullptr;
|
||||
}
|
||||
|
||||
//Push our log every 15s:
|
||||
if (framesSinceLastFlush >= 900) {
|
||||
Game::logger->Flush();
|
||||
framesSinceLastFlush = 0;
|
||||
}
|
||||
else
|
||||
framesSinceLastFlush++;
|
||||
|
||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||
if (framesSinceLastSQLPing >= 40000) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
int masterPort;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
}
|
||||
else
|
||||
framesSinceLastSQLPing++;
|
||||
|
||||
//10m shutdown for universe kill command
|
||||
if (shouldShutdown) {
|
||||
if (framesSinceKillUniverseCommand >= 40000) {
|
||||
std::exit(0);
|
||||
}
|
||||
else
|
||||
framesSinceKillUniverseCommand++;
|
||||
}
|
||||
|
||||
const auto instances = Game::im->GetInstances();
|
||||
|
||||
for (auto* instance : instances) {
|
||||
if (instance == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto affirmTimeout = instance->GetAffirmationTimeout();
|
||||
|
||||
if (!instance->GetPendingAffirmations().empty()) {
|
||||
affirmTimeout++;
|
||||
}
|
||||
else {
|
||||
affirmTimeout = 0;
|
||||
}
|
||||
|
||||
instance->SetAffirmationTimeout(affirmTimeout);
|
||||
|
||||
if (affirmTimeout == 1000) {
|
||||
instance->Shutdown();
|
||||
instance->SetShutdownComplete(true);
|
||||
|
||||
Game::im->RedirectPendingRequests(instance);
|
||||
}
|
||||
}
|
||||
|
||||
//Remove dead instances
|
||||
for (auto* instance : instances) {
|
||||
if (instance == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (instance->GetShutdownComplete()) {
|
||||
Game::im->RemoveInstance(instance);
|
||||
}
|
||||
}
|
||||
|
||||
t += std::chrono::milliseconds(highFrameRate);
|
||||
std::this_thread::sleep_until(t);
|
||||
}
|
||||
|
||||
//Delete our objects here:
|
||||
Database::Destroy();
|
||||
delete Game::im;
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dLogger* SetupLogger() {
|
||||
std::string logPath =
|
||||
"./logs/MasterServer_" + std::to_string(time(nullptr)) + ".log";
|
||||
bool logToConsole = false;
|
||||
#ifdef _DEBUG
|
||||
logToConsole = true;
|
||||
#endif
|
||||
|
||||
return new dLogger(logPath, logToConsole);
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) {
|
||||
Game::logger->Log("MasterServer", "A server has disconnected\n");
|
||||
|
||||
//Since this disconnection is intentional, we'll just delete it as
|
||||
//we'll start a new one anyway if needed:
|
||||
Instance* instance =
|
||||
Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
if (instance) {
|
||||
Game::im->RemoveInstance(instance); //Delete the old
|
||||
}
|
||||
}
|
||||
|
||||
if (packet->data[0] == ID_CONNECTION_LOST) {
|
||||
Game::logger->Log("MasterServer", "A server has lost the connection\n");
|
||||
|
||||
Instance* instance =
|
||||
Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
if (instance) {
|
||||
LWOZONEID zoneID = instance->GetZoneID(); //Get the zoneID so we can recreate a server
|
||||
Game::im->RemoveInstance(instance); //Delete the old
|
||||
//Game::im->GetInstance(zoneID.GetMapID(), false, 0); //Create the new
|
||||
}
|
||||
}
|
||||
|
||||
if (packet->data[1] == MASTER) {
|
||||
switch (packet->data[3]) {
|
||||
case MSG_MASTER_REQUEST_PERSISTENT_ID: {
|
||||
Game::logger->Log("MasterServer", "A persistent ID req\n");
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
uint64_t requestID = 0;
|
||||
inStream.Read(requestID);
|
||||
|
||||
uint32_t objID = ObjectIDManager::Instance()->GeneratePersistentID();
|
||||
MasterPackets::SendPersistentIDResponse(Game::server, packet->systemAddress, requestID, objID);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_REQUEST_ZONE_TRANSFER: {
|
||||
Game::logger->Log("MasterServer","Received zone transfer req\n");
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
uint64_t requestID = 0;
|
||||
uint8_t mythranShift = false;
|
||||
uint32_t zoneID = 0;
|
||||
uint32_t zoneClone = 0;
|
||||
|
||||
inStream.Read(requestID);
|
||||
inStream.Read(mythranShift);
|
||||
inStream.Read(zoneID);
|
||||
inStream.Read(zoneClone);
|
||||
|
||||
Instance* in = Game::im->GetInstance(zoneID, false, zoneClone);
|
||||
|
||||
for (auto* instance : Game::im->GetInstances()) {
|
||||
Game::logger->Log("MasterServer", "Instance: %i/%i/%i -> %i\n",instance->GetMapID(), instance->GetCloneID(),instance->GetInstanceID(), instance == in);
|
||||
}
|
||||
|
||||
if (!in->GetIsReady()) //Instance not ready, make a pending request
|
||||
{
|
||||
in->GetPendingRequests().push_back({ requestID, static_cast<bool>(mythranShift), packet->systemAddress });
|
||||
Game::logger->Log("MasterServer", "Server not ready, adding pending request %llu %i %i\n", requestID, zoneID, zoneClone);
|
||||
break;
|
||||
}
|
||||
|
||||
//Instance is ready, transfer
|
||||
Game::logger->Log("MasterServer", "Responding to transfer request %llu for zone %i %i\n", requestID, zoneID, zoneClone);
|
||||
Game::im->RequestAffirmation(in, { requestID, static_cast<bool>(mythranShift), packet->systemAddress });
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_SERVER_INFO: {
|
||||
//MasterPackets::HandleServerInfo(packet);
|
||||
|
||||
//This is here because otherwise we'd have to include IM in
|
||||
//non-master servers. This packet allows us to add World
|
||||
//servers back if master crashed
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
uint32_t theirPort = 0;
|
||||
uint32_t theirZoneID = 0;
|
||||
uint32_t theirInstanceID = 0;
|
||||
ServerType theirServerType;
|
||||
std::string theirIP = "";
|
||||
|
||||
inStream.Read(theirPort);
|
||||
inStream.Read(theirZoneID);
|
||||
inStream.Read(theirInstanceID);
|
||||
inStream.Read(theirServerType);
|
||||
theirIP = PacketUtils::ReadString(24, packet, false); //24 is the current offset
|
||||
|
||||
if (theirServerType == ServerType::World && !Game::im->IsPortInUse(theirPort)) {
|
||||
Instance* in = new Instance(theirIP, theirPort, theirZoneID, theirInstanceID, 0, 12, 12);
|
||||
|
||||
SystemAddress copy;
|
||||
copy.binaryAddress = packet->systemAddress.binaryAddress;
|
||||
copy.port = packet->systemAddress.port;
|
||||
|
||||
in->SetSysAddr(copy);
|
||||
Game::im->AddInstance(in);
|
||||
}
|
||||
else {
|
||||
auto instance = Game::im->FindInstance(
|
||||
theirZoneID, static_cast<uint16_t>(theirInstanceID));
|
||||
if (instance) {
|
||||
instance->SetSysAddr(packet->systemAddress);
|
||||
}
|
||||
}
|
||||
|
||||
Game::logger->Log("MasterServer", "Received server info, instance: %i port: %i\n", theirInstanceID, theirPort);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_SET_SESSION_KEY: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
uint32_t sessionKey = 0;
|
||||
std::string username;
|
||||
|
||||
inStream.Read(sessionKey);
|
||||
username = PacketUtils::ReadString(12, packet, false);
|
||||
|
||||
for (auto it : activeSessions) {
|
||||
if (it.second == username) {
|
||||
activeSessions.erase(it.first);
|
||||
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_NEW_SESSION_ALERT);
|
||||
bitStream.Write(sessionKey);
|
||||
bitStream.Write(RakNet::RakString(username.c_str()));
|
||||
SEND_PACKET_BROADCAST;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
activeSessions.insert(std::make_pair(sessionKey, username));
|
||||
Game::logger->Log("MasterServer", "Got sessionKey %i for user %s\n", sessionKey, username.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_REQUEST_SESSION_KEY: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
std::string username = PacketUtils::ReadString(8, packet, false);
|
||||
|
||||
for (auto key : activeSessions) {
|
||||
if (key.second == username) {
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_SESSION_KEY_RESPONSE);
|
||||
bitStream.Write(key.first);
|
||||
PacketUtils::WriteString(bitStream, key.second, 64);
|
||||
Game::server->Send(&bitStream, packet->systemAddress, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_PLAYER_ADDED: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
LWOMAPID theirZoneID = 0;
|
||||
LWOINSTANCEID theirInstanceID = 0;
|
||||
|
||||
inStream.Read(theirZoneID);
|
||||
inStream.Read(theirInstanceID);
|
||||
|
||||
auto instance =
|
||||
Game::im->FindInstance(theirZoneID, theirInstanceID);
|
||||
if (instance) {
|
||||
instance->AddPlayer(Player());
|
||||
}
|
||||
else {
|
||||
printf("Instance missing? What?\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_PLAYER_REMOVED: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
LWOMAPID theirZoneID = 0;
|
||||
LWOINSTANCEID theirInstanceID = 0;
|
||||
|
||||
inStream.Read(theirZoneID);
|
||||
inStream.Read(theirInstanceID);
|
||||
|
||||
auto instance =
|
||||
Game::im->FindInstance(theirZoneID, theirInstanceID);
|
||||
if (instance) {
|
||||
instance->RemovePlayer(Player());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_CREATE_PRIVATE_ZONE: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
uint32_t mapId;
|
||||
LWOCLONEID cloneId;
|
||||
RakNet::RakString password;
|
||||
|
||||
inStream.Read(mapId);
|
||||
inStream.Read(cloneId);
|
||||
inStream.Read(password);
|
||||
|
||||
Game::im->CreatePrivateInstance(mapId, cloneId,
|
||||
password.C_String());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_REQUEST_PRIVATE_ZONE: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
uint64_t requestID = 0;
|
||||
uint8_t mythranShift = false;
|
||||
|
||||
RakNet::RakString password;
|
||||
|
||||
inStream.Read(requestID);
|
||||
inStream.Read(mythranShift);
|
||||
inStream.Read(password);
|
||||
|
||||
auto* instance = Game::im->FindPrivateInstance(password.C_String());
|
||||
|
||||
Game::logger->Log( "MasterServer", "Join private zone: %llu %d %s %p\n", requestID, mythranShift, password.C_String(), instance);
|
||||
|
||||
if (instance == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& zone = instance->GetZoneID();
|
||||
|
||||
MasterPackets::SendZoneTransferResponse(Game::server, packet->systemAddress, requestID,(bool)mythranShift, zone.GetMapID(),instance->GetInstanceID(), zone.GetCloneID(),instance->GetIP(), instance->GetPort());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_WORLD_READY: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
LWOMAPID zoneID;
|
||||
LWOINSTANCEID instanceID;
|
||||
|
||||
inStream.Read(zoneID);
|
||||
inStream.Read(instanceID);
|
||||
|
||||
Game::logger->Log("MasterServer", "Got world ready %i %i\n",zoneID, instanceID);
|
||||
|
||||
auto* instance = Game::im->FindInstance(zoneID, instanceID);
|
||||
|
||||
if (instance == nullptr) {
|
||||
Game::logger->Log("MasterServer","Failed to find zone to ready\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::logger->Log("MasterServer", "Ready zone %i\n", zoneID);
|
||||
Game::im->ReadyInstance(instance);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_PREP_ZONE: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
int zoneID;
|
||||
inStream.Read(zoneID);
|
||||
|
||||
Game::logger->Log("MasterServer", "Prepping zone %i\n", zoneID);
|
||||
Game::im->GetInstance(zoneID, false, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_AFFIRM_TRANSFER_RESPONSE: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
uint64_t requestID;
|
||||
|
||||
inStream.Read(requestID);
|
||||
|
||||
Game::logger->Log("MasterServer","Got affirmation of transfer %llu\n",requestID);
|
||||
|
||||
auto* instance =Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
|
||||
if (instance == nullptr)
|
||||
return;
|
||||
|
||||
Game::im->AffirmTransfer(instance, requestID);
|
||||
Game::logger->Log("MasterServer", "Affirmation complete %llu\n",requestID);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_SHUTDOWN_RESPONSE: {
|
||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
uint64_t header = inStream.Read(header);
|
||||
|
||||
auto* instance =Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
|
||||
if (instance == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Game::logger->Log("MasterServer", "Got shutdown response\n");
|
||||
instance->SetShutdownComplete(true);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_MASTER_SHUTDOWN_UNIVERSE: {
|
||||
Game::logger->Log("MasterServer","Received shutdown universe command, ""shutting down in 10 minutes.\n");
|
||||
shouldShutdown = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Game::logger->Log("MasterServer","Unknown master packet ID from server: %i\n",packet->data[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownSequence() {
|
||||
if (shutdownSequenceStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
shutdownSequenceStarted = true;
|
||||
|
||||
if (Game::im) {
|
||||
for (auto* instance : Game::im->GetInstances()) {
|
||||
if (instance == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
instance->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
auto* objIdManager = ObjectIDManager::TryInstance();
|
||||
if (objIdManager != nullptr) {
|
||||
objIdManager->SaveToDatabase();
|
||||
printf("Saved objidtracker...\n");
|
||||
}
|
||||
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
auto ticks = 0;
|
||||
|
||||
if (!Game::im) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
printf("Attempting to shutdown instances, max 60 seconds...\n");
|
||||
while (true) {
|
||||
auto done = true;
|
||||
|
||||
for (auto* instance : Game::im->GetInstances()) {
|
||||
if (instance == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!instance->GetShutdownComplete()) {
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
t += std::chrono::milliseconds(highFrameRate);
|
||||
std::this_thread::sleep_until(t);
|
||||
|
||||
ticks++;
|
||||
|
||||
if (ticks == 600 * 6) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
71
dMasterServer/ObjectIDManager.cpp
Normal file
71
dMasterServer/ObjectIDManager.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "ObjectIDManager.h"
|
||||
|
||||
// Custom Classes
|
||||
#include "Database.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
// Static Variables
|
||||
ObjectIDManager *ObjectIDManager::m_Address = nullptr;
|
||||
|
||||
//! Initializes the manager
|
||||
void ObjectIDManager::Initialize(dLogger *logger) {
|
||||
this->mLogger = logger;
|
||||
this->currentPersistentID = 1;
|
||||
|
||||
try {
|
||||
sql::PreparedStatement *stmt = Database::CreatePreppedStmt(
|
||||
"SELECT last_object_id FROM object_id_tracker");
|
||||
|
||||
sql::ResultSet *result = stmt->executeQuery();
|
||||
auto next = result->next();
|
||||
|
||||
if (!next) {
|
||||
sql::PreparedStatement *insertStmt = Database::CreatePreppedStmt(
|
||||
"INSERT INTO object_id_tracker VALUES (1)");
|
||||
|
||||
insertStmt->execute();
|
||||
|
||||
delete insertStmt;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (next) {
|
||||
this->currentPersistentID =
|
||||
result->getInt(1) > 0 ? result->getInt(1) : 1;
|
||||
next = result->next();
|
||||
}
|
||||
|
||||
delete result;
|
||||
delete stmt;
|
||||
} catch (sql::SQLException &e) {
|
||||
mLogger->Log("ObjectIDManager", "Unable to fetch max persistent object "
|
||||
"ID in use. Defaulting to 1.\n");
|
||||
mLogger->Log("ObjectIDManager", "SQL error: %s\n", e.what());
|
||||
this->currentPersistentID = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//! Generates a new persistent ID
|
||||
uint32_t ObjectIDManager::GeneratePersistentID(void) {
|
||||
uint32_t toReturn = ++this->currentPersistentID;
|
||||
|
||||
// So we peroidically save our ObjID to the database:
|
||||
if (toReturn % 25 == 0) { // TEMP: DISABLED FOR DEBUG / DEVELOPMENT!
|
||||
sql::PreparedStatement *stmt = Database::CreatePreppedStmt(
|
||||
"UPDATE object_id_tracker SET last_object_id=?");
|
||||
stmt->setUInt(1, toReturn);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
void ObjectIDManager::SaveToDatabase() {
|
||||
sql::PreparedStatement *stmt = Database::CreatePreppedStmt(
|
||||
"UPDATE object_id_tracker SET last_object_id=?");
|
||||
stmt->setUInt(1, currentPersistentID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
47
dMasterServer/ObjectIDManager.h
Normal file
47
dMasterServer/ObjectIDManager.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
// C++
|
||||
#include <cstdint>
|
||||
|
||||
class dLogger;
|
||||
|
||||
/*!
|
||||
\file ObjectIDManager.hpp
|
||||
\brief A manager that handles requests for object IDs
|
||||
*/
|
||||
|
||||
//! The Object ID Manager
|
||||
class ObjectIDManager {
|
||||
private:
|
||||
dLogger* mLogger;
|
||||
static ObjectIDManager * m_Address; //!< The singleton instance
|
||||
|
||||
uint32_t currentPersistentID; //!< The highest current persistent ID in use
|
||||
|
||||
public:
|
||||
|
||||
//! Return the singleton if it is initialized
|
||||
static ObjectIDManager* TryInstance() {
|
||||
return m_Address;
|
||||
}
|
||||
|
||||
//! The singleton method
|
||||
static ObjectIDManager * Instance() {
|
||||
if (m_Address == nullptr) {
|
||||
m_Address = new ObjectIDManager;
|
||||
}
|
||||
|
||||
return m_Address;
|
||||
}
|
||||
|
||||
//! Initializes the manager
|
||||
void Initialize(dLogger* logger);
|
||||
|
||||
//! Generates a new persistent ID
|
||||
/*!
|
||||
\return The new persistent ID
|
||||
*/
|
||||
uint32_t GeneratePersistentID(void);
|
||||
|
||||
void SaveToDatabase();
|
||||
};
|
||||
Reference in New Issue
Block a user