Merge branch 'main' into main

This commit is contained in:
Aaron Kimbrell
2022-05-24 19:00:52 -05:00
committed by GitHub
169 changed files with 3164 additions and 1954 deletions

View File

@@ -2,123 +2,99 @@
#include "UserManager.h"
#define HIGH 16
#define MEDIUM 33
#define LOW 66
//Times are 1 / fps, in ms
#define HIGH 16 //60 fps
#define MEDIUM 33 //30 fps
#define LOW 66 //15 fps
#define SOCIAL { MEDIUM, LOW }
#define BATTLE { HIGH, MEDIUM }
#define BATTLE_INSTANCE { MEDIUM, LOW }
#define RACE { MEDIUM, LOW }
#define PROPERTY { LOW, LOW }
#define SOCIAL { LOW }
#define SOCIAL_HUB { MEDIUM } //Added to compensate for the large playercounts in NS and NT
#define BATTLE { HIGH }
#define BATTLE_INSTANCE { MEDIUM }
#define RACE { HIGH }
#define PROPERTY { LOW }
PerformanceProfile PerformanceManager::m_CurrentProfile = SOCIAL;
PerformanceProfile PerformanceManager::m_DefaultProfile = SOCIAL;
PerformanceProfile PerformanceManager::m_InactiveProfile = { LOW, LOW };
PerformanceProfile PerformanceManager::m_InactiveProfile = { LOW };
std::map<LWOMAPID, PerformanceProfile> PerformanceManager::m_Profiles = {
// VE
{ 1000, SOCIAL },
std::map<LWOMAPID, PerformanceProfile> PerformanceManager::m_Profiles = {
// VE
{ 1000, SOCIAL },
// AG
{ 1100, BATTLE },
{ 1101, BATTLE_INSTANCE },
{ 1102, BATTLE_INSTANCE },
{ 1150, PROPERTY },
{ 1151, PROPERTY },
// AG
{ 1100, BATTLE },
{ 1101, BATTLE_INSTANCE },
{ 1102, BATTLE_INSTANCE },
{ 1150, PROPERTY },
{ 1151, PROPERTY },
// NS
{ 1200, SOCIAL },
{ 1201, SOCIAL },
{ 1203, RACE },
{ 1204, BATTLE_INSTANCE },
{ 1250, PROPERTY },
{ 1251, PROPERTY },
// NS
{ 1200, SOCIAL_HUB },
{ 1201, SOCIAL },
{ 1203, RACE },
{ 1204, BATTLE_INSTANCE },
{ 1250, PROPERTY },
{ 1251, PROPERTY },
// GF
{ 1300, BATTLE },
{ 1302, BATTLE_INSTANCE },
{ 1303, BATTLE_INSTANCE },
{ 1350, PROPERTY },
// GF
{ 1300, BATTLE },
{ 1302, BATTLE_INSTANCE },
{ 1303, BATTLE_INSTANCE },
{ 1350, PROPERTY },
// FV
{ 1400, BATTLE },
{ 1402, BATTLE_INSTANCE },
{ 1403, RACE },
{ 1450, PROPERTY },
// FV
{ 1400, BATTLE },
{ 1402, BATTLE_INSTANCE },
{ 1403, RACE },
{ 1450, PROPERTY },
// LUP
{ 1600, SOCIAL },
{ 1601, SOCIAL },
{ 1602, SOCIAL },
{ 1603, SOCIAL },
{ 1604, SOCIAL },
// LUP
{ 1600, SOCIAL },
{ 1601, SOCIAL },
{ 1602, SOCIAL },
{ 1603, SOCIAL },
{ 1604, SOCIAL },
// LEGO Club
{ 1700, SOCIAL },
// LEGO Club
{ 1700, SOCIAL },
// AM
{ 1800, BATTLE },
// AM
{ 1800, BATTLE },
// NT
{ 1900, SOCIAL },
// NT
{ 1900, SOCIAL_HUB },
// NJ
{ 2000, BATTLE },
{ 2001, BATTLE_INSTANCE },
// NJ
{ 2000, BATTLE },
{ 2001, BATTLE_INSTANCE },
};
PerformanceManager::PerformanceManager()
{
PerformanceManager::PerformanceManager() {
}
PerformanceManager::~PerformanceManager()
{
PerformanceManager::~PerformanceManager() {
}
void PerformanceManager::SelectProfile(LWOMAPID mapID)
{
const auto pair = m_Profiles.find(mapID);
void PerformanceManager::SelectProfile(LWOMAPID mapID) {
const auto pair = m_Profiles.find(mapID);
if (pair == m_Profiles.end())
{
m_CurrentProfile = m_DefaultProfile;
if (pair == m_Profiles.end()) {
m_CurrentProfile = m_DefaultProfile;
return;
}
return;
}
m_CurrentProfile = pair->second;
m_CurrentProfile = pair->second;
}
uint32_t PerformanceManager::GetServerFramerate()
{
if (UserManager::Instance()->GetUserCount() == 0)
{
return m_InactiveProfile.serverFramerate;
}
uint32_t PerformanceManager::GetServerFramerate() {
if (UserManager::Instance()->GetUserCount() == 0) {
return m_InactiveProfile.serverFramerate;
}
return m_CurrentProfile.serverFramerate;
}
uint32_t PerformanceManager::GetPhysicsFramerate()
{
if (UserManager::Instance()->GetUserCount() == 0)
{
return m_InactiveProfile.physicsFramerate;
}
return m_CurrentProfile.physicsFramerate;
}
uint32_t PerformanceManager::GetPhysicsStepRate()
{
if (UserManager::Instance()->GetUserCount() == 0)
{
return 10; // Row physics at a really low framerate if the server is empty
}
return m_CurrentProfile.physicsFramerate / m_CurrentProfile.serverFramerate;
}
return m_CurrentProfile.serverFramerate;
}

View File

@@ -4,29 +4,24 @@
#include "dCommonVars.h"
struct PerformanceProfile
{
uint32_t serverFramerate;
uint32_t physicsFramerate;
struct PerformanceProfile {
uint32_t serverFramerate;
};
class PerformanceManager
{
class PerformanceManager {
public:
~PerformanceManager();
~PerformanceManager();
static void SelectProfile(LWOMAPID mapID);
static void SelectProfile(LWOMAPID mapID);
static uint32_t GetServerFramerate();
static uint32_t GetPhysicsFramerate();
static uint32_t GetPhysicsStepRate();
static uint32_t GetServerFramerate();
private:
PerformanceManager();
static PerformanceProfile m_CurrentProfile;
static PerformanceProfile m_DefaultProfile;
static PerformanceProfile m_InactiveProfile;
static std::map<LWOMAPID, PerformanceProfile> m_Profiles;
PerformanceManager();
static PerformanceProfile m_CurrentProfile;
static PerformanceProfile m_DefaultProfile;
static PerformanceProfile m_InactiveProfile;
static std::map<LWOMAPID, PerformanceProfile> m_Profiles;
};

View File

@@ -77,6 +77,9 @@ bool chatConnected = false;
bool worldShutdownSequenceStarted = false;
bool worldShutdownSequenceComplete = false;
void WorldShutdownSequence();
void WorldShutdownProcess(uint32_t zoneId);
void FinalizeShutdown();
void SendShutdownMessageToMaster();
dLogger* SetupLogger(int zoneID, int instanceID);
void HandlePacketChat(Packet* packet);
@@ -99,11 +102,9 @@ int main(int argc, char** argv) {
// Triggers the shutdown sequence at application exit
std::atexit(WorldShutdownSequence);
signal(SIGINT, [](int)
{
WorldShutdownSequence();
});
signal(SIGINT, [](int){ WorldShutdownSequence(); });
signal(SIGTERM, [](int){ WorldShutdownSequence(); });
int zoneID = 1000;
int cloneID = 0;
@@ -124,7 +125,7 @@ int main(int argc, char** argv) {
//Create all the objects we need to run our service:
Game::logger = SetupLogger(zoneID, instanceID);
if (!Game::logger) return 0;
Game::logger->SetLogToConsole(true); //We want this info to always be logged.
Game::logger->Log("WorldServer", "Starting World server...\n");
Game::logger->Log("WorldServer", "Version: %i.%i\n", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
@@ -150,7 +151,7 @@ int main(int argc, char** argv) {
Game::logger->Log("WorldServer", "Error Code: %i\n", e.errorCode());
return -1;
}
CDClientManager::Instance()->Initialize();
//Connect to the MySQL Database
@@ -196,7 +197,7 @@ int main(int argc, char** argv) {
//Connect to the chat server:
int chatPort = 1501;
if (config.GetValue("chat_server_port") != "") chatPort = std::atoi(config.GetValue("chat_server_port").c_str());
auto chatSock = SocketDescriptor(uint16_t(ourPort + 2), 0);
Game::chatServer = RakNetworkFactory::GetRakPeerInterface();
Game::chatServer->Startup(1, 30, &chatSock, 1);
@@ -217,7 +218,7 @@ int main(int argc, char** argv) {
int framesSinceLastUsersSave = 0;
int framesSinceLastSQLPing = 0;
int framesSinceLastUser = 0;
const float maxPacketProcessingTime = 1.5f; //0.015f;
const int maxPacketsToProcess = 1024;
@@ -225,9 +226,7 @@ int main(int argc, char** argv) {
int framesSinceMasterStatus = 0;
int framesSinceShutdownSequence = 0;
int currentFramerate = highFrameRate;
int physicsFramerate = highFrameRate;
int physicsStepRate = 0;
int physicsStepCount = 0;
int ghostingStepCount = 0;
auto ghostingLastTime = std::chrono::high_resolution_clock::now();
@@ -250,7 +249,7 @@ int main(int argc, char** argv) {
"res/CDClient.fdb",
"res/cdclient.fdb",
};
for (const auto& file : aliases) {
fileStream.open(file, std::ios::binary | std::ios::in);
if (fileStream.is_open()) {
@@ -260,7 +259,7 @@ int main(int argc, char** argv) {
const int bufferSize = 1024;
MD5* md5 = new MD5();
char fileStreamBuffer[1024] = {};
while (!fileStream.eof()) {
@@ -275,7 +274,7 @@ int main(int argc, char** argv) {
md5->update(nullTerminateBuffer, 1); // null terminate the data
md5->finalize();
databaseChecksum = md5->hexdigest();
delete md5;
Game::logger->Log("WorldServer", "FDB Checksum calculated as: %s\n", databaseChecksum.c_str());
@@ -302,9 +301,6 @@ int main(int argc, char** argv) {
{
currentFramerate = PerformanceManager::GetServerFramerate();
}
physicsFramerate = PerformanceManager::GetPhysicsFramerate();
physicsStepRate = PerformanceManager::GetPhysicsStepRate();
//Warning if we ran slow
if (deltaTime > currentFramerate) {
@@ -315,7 +311,9 @@ int main(int argc, char** argv) {
if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++;
if (framesSinceMasterDisconnect >= 30) {
int framesToWaitForMaster = ready ? 10 : 200;
if (framesSinceMasterDisconnect >= framesToWaitForMaster && !worldShutdownSequenceStarted) {
Game::logger->Log("WorldServer", "Game loop running but no connection to master for %d frames, shutting down\n", framesToWaitForMaster);
worldShutdownSequenceStarted = true;
}
}
@@ -338,10 +336,7 @@ int main(int argc, char** argv) {
if (zoneID != 0 && deltaTime > 0.0f) {
Metrics::StartMeasurement(MetricVariable::Physics);
if (physicsStepCount++ >= physicsStepRate) {
dpWorld::Instance().StepWorld(deltaTime);
physicsStepCount = 0;
}
dpWorld::Instance().StepWorld(deltaTime);
Metrics::EndMeasurement(MetricVariable::Physics);
Metrics::StartMeasurement(MetricVariable::UpdateEntities);
@@ -365,7 +360,7 @@ int main(int argc, char** argv) {
//Check for packets here:
packet = Game::server->ReceiveFromMaster();
if (packet) { //We can get messages not handle-able by the dServer class, so handle them if we returned anything.
HandlePacket(packet);
HandlePacket(packet);
Game::server->DeallocateMasterPacket(packet);
}
@@ -464,13 +459,13 @@ int main(int argc, char** argv) {
t += std::chrono::milliseconds(currentFramerate);
std::this_thread::sleep_until(t);
Metrics::EndMeasurement(MetricVariable::Sleep);
if (!ready && Game::server->GetIsConnectedToMaster())
{
// Some delay is required here or else we crash the client?
framesSinceMasterStatus++;
if (framesSinceMasterStatus >= 200)
@@ -483,80 +478,16 @@ int main(int argc, char** argv) {
}
}
if (worldShutdownSequenceStarted && !worldShutdownSequenceComplete)
{
if (framesSinceShutdownSequence == 0) {
ChatPackets::SendSystemMessage(UNASSIGNED_SYSTEM_ADDRESS, u"Server shutting down...", true);
for (auto i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i)
{
const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i);
auto* entity = Player::GetPlayer(player);
if (entity != nullptr && entity->GetCharacter() != nullptr)
{
auto* skillComponent = entity->GetComponent<SkillComponent>();
if (skillComponent != nullptr)
{
skillComponent->Reset();
}
entity->GetCharacter()->SaveXMLToDatabase();
}
}
if (PropertyManagementComponent::Instance() != nullptr) {
ChatPackets::SendSystemMessage(UNASSIGNED_SYSTEM_ADDRESS, u"Property data saved...", true);
PropertyManagementComponent::Instance()->Save();
}
ChatPackets::SendSystemMessage(UNASSIGNED_SYSTEM_ADDRESS, u"Character data saved...", true);
}
framesSinceShutdownSequence++;
if (framesSinceShutdownSequence == 100)
{
while (Game::server->GetReplicaManager()->GetParticipantCount() > 0)
{
const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(0);
Game::server->Disconnect(player, SERVER_DISCON_KICK);
}
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_SHUTDOWN_RESPONSE);
Game::server->SendToMaster(&bitStream);
}
if (framesSinceShutdownSequence == 300)
{
break;
}
if (worldShutdownSequenceStarted && !worldShutdownSequenceComplete) {
WorldShutdownProcess(zoneID);
break;
}
Metrics::AddMeasurement(MetricVariable::CPUTime, (1e6 * (1000.0 * (std::clock() - metricCPUTimeStart))) / CLOCKS_PER_SEC);
Metrics::EndMeasurement(MetricVariable::Frame);
}
//Delete our objects here:
if (Game::physicsWorld) Game::physicsWorld = nullptr;
if (Game::zoneManager) delete Game::zoneManager;
Game::logger->Log("WorldServer", "Shutdown complete, zone (%i), instance (%i)\n", Game::server->GetZoneID(), instanceID);
Metrics::Clear();
Database::Destroy();
delete Game::chatFilter;
delete Game::server;
delete Game::logger;
worldShutdownSequenceComplete = true;
exit(0);
FinalizeShutdown();
return EXIT_SUCCESS;
}
dLogger * SetupLogger(int zoneID, int instanceID) {
@@ -574,7 +505,7 @@ dLogger * SetupLogger(int zoneID, int instanceID) {
void HandlePacketChat(Packet* packet) {
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
Game::logger->Log("WorldServer", "Lost our connection to chat, zone(%i), instance(%i)\n", Game::server->GetZoneID(), Game::server->GetInstanceID());
chatConnected = false;
}
@@ -648,7 +579,7 @@ void HandlePacketChat(Packet* packet) {
inStream.Read(playerId);
inStream.Read(playerId);
inStream.Read(expire);
auto* entity = EntityManager::Instance()->GetEntity(playerId);
if (entity != nullptr)
@@ -898,7 +829,7 @@ void HandlePacket(Packet* packet) {
}
if (packet->data[1] != WORLD) return;
switch (packet->data[3]) {
case MSG_WORLD_CLIENT_VALIDATION: {
std::string username = PacketUtils::ReadString(0x08, packet, true);
@@ -913,7 +844,7 @@ void HandlePacket(Packet* packet) {
uint32_t gmLevel = 0;
auto* stmt = Database::CreatePreppedStmt("SELECT gm_level FROM accounts WHERE name=? LIMIT 1;");
stmt->setString(1, username.c_str());
auto* res = stmt->executeQuery();
while (res->next()) {
gmLevel = res->getInt(1);
@@ -929,7 +860,7 @@ void HandlePacket(Packet* packet) {
return;
}
}
//Request the session info from Master:
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_REQUEST_SESSION_KEY);
@@ -941,7 +872,7 @@ void HandlePacket(Packet* packet) {
info.sysAddr = SystemAddress(packet->systemAddress);
info.hash = sessionKey;
m_PendingUsers.insert(std::make_pair(username, info));
break;
}
@@ -954,7 +885,7 @@ void HandlePacket(Packet* packet) {
}
//This loops prevents users who aren't authenticated to double-request the char list, which
//would make the login screen freeze sometimes.
//would make the login screen freeze sometimes.
if (m_PendingUsers.size() > 0) {
for (auto it : m_PendingUsers) {
if (it.second.sysAddr == packet->systemAddress) {
@@ -969,18 +900,18 @@ void HandlePacket(Packet* packet) {
case MSG_WORLD_CLIENT_GAME_MSG: {
RakNet::BitStream bitStream(packet->data, packet->length, false);
uint64_t header;
LWOOBJID objectID;
uint16_t messageID;
bitStream.Read(header);
bitStream.Read(objectID);
bitStream.Read(messageID);
RakNet::BitStream dataStream;
bitStream.Read(dataStream, bitStream.GetNumberOfUnreadBits());
GameMessageHandler::HandleMessage(&dataStream, packet->systemAddress, objectID, GAME_MSG(messageID));
break;
}
@@ -993,7 +924,7 @@ void HandlePacket(Packet* packet) {
case MSG_WORLD_CLIENT_LOGIN_REQUEST: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
LWOOBJID playerID = 0;
inStream.Read(playerID);
playerID = GeneralUtils::ClearBit(playerID, OBJECT_BIT_CHARACTER);
@@ -1008,7 +939,7 @@ void HandlePacket(Packet* packet) {
UserManager::Instance()->RequestCharacterList(packet->systemAddress);
break;
}
case MSG_WORLD_CLIENT_CHARACTER_RENAME_REQUEST: {
UserManager::Instance()->RenameCharacter(packet->systemAddress, packet);
break;
@@ -1019,32 +950,37 @@ void HandlePacket(Packet* packet) {
User* user = UserManager::Instance()->GetUser(packet->systemAddress);
if (user) {
Character* c = user->GetLastUsedChar();
if (c != nullptr) {
if (c != nullptr) {
std::u16string username = GeneralUtils::ASCIIToUTF16(c->GetName());
WorldPackets::SendCreateCharacter(packet->systemAddress, c->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel());
WorldPackets::SendServerState(packet->systemAddress);
Game::server->GetReplicaManager()->AddParticipant(packet->systemAddress);
EntityInfo info {};
info.lot = 1;
Entity* player = EntityManager::Instance()->CreateEntity(info, UserManager::Instance()->GetUser(packet->systemAddress));
WorldPackets::SendCreateCharacter(packet->systemAddress, player, c->GetXMLData(), username, c->GetGMLevel());
WorldPackets::SendServerState(packet->systemAddress);
const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(dZoneManager::Instance()->GetZone()->GetWorldID());
EntityManager::Instance()->ConstructEntity(player, UNASSIGNED_SYSTEM_ADDRESS, true);
if (respawnPoint != NiPoint3::ZERO)
{
GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, NiQuaternion::IDENTITY);
}
EntityManager::Instance()->ConstructAllEntities(packet->systemAddress);
player->GetComponent<CharacterComponent>()->SetLastRocketConfig(u"");
EntityManager::Instance()->ConstructAllEntities(packet->systemAddress);
auto* characterComponent = player->GetComponent<CharacterComponent>();
if (characterComponent) {
player->GetComponent<CharacterComponent>()->RocketUnEquip(player);
}
c->SetRetroactiveFlags();
player->RetroactiveVaultSize();
player->GetCharacter()->SetTargetScene("");
// Fix the destroyable component
@@ -1079,7 +1015,7 @@ void HandlePacket(Packet* packet) {
int templateId = result.getIntField(0);
result.finalize();
auto* propertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE template_id = ? AND clone_id = ?;");
propertyLookup->setInt(1, templateId);
@@ -1104,7 +1040,7 @@ void HandlePacket(Packet* packet) {
stmtL->setUInt(1, res->getUInt(1));
auto lxres = stmtL->executeQuery();
while (lxres->next()) {
auto lxfml = lxres->getBlob(1);
@@ -1156,7 +1092,7 @@ void HandlePacket(Packet* packet) {
noBBB:
// Tell the client it's done loading:
GameMessages::SendInvalidZoneTransferList(player, packet->systemAddress, u"https://forms.zohopublic.eu/virtualoffice204/form/DLUInGameSurvey/formperma/kpU-IL5v2-Wt41QcB5UFnYjzlLp-j2LEisF8e11PisU", u"", false, false);
GameMessages::SendInvalidZoneTransferList(player, packet->systemAddress, GeneralUtils::ASCIIToUTF16(Game::config->GetValue("source")), u"", false, false);
GameMessages::SendServerDoneLoadingAllObjects(player, packet->systemAddress);
//Send the player it's mail count:
@@ -1176,9 +1112,9 @@ void HandlePacket(Packet* packet) {
{
bitStream.Write(playerName[i]);
}
//bitStream.Write(playerName);
auto zone = dZoneManager::Instance()->GetZone()->GetZoneID();
bitStream.Write(zone.GetMapID());
bitStream.Write(zone.GetInstanceID());
@@ -1287,35 +1223,75 @@ void HandlePacket(Packet* packet) {
}
}
void WorldShutdownSequence()
{
if (worldShutdownSequenceStarted || worldShutdownSequenceComplete)
{
return;
}
void WorldShutdownProcess(uint32_t zoneId) {
Game::logger->Log("WorldServer", "Saving map %i instance %i\n", zoneId, instanceID);
for (auto i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i) {
const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i);
worldShutdownSequenceStarted = true;
auto* entity = Player::GetPlayer(player);
Game::logger->Log("WorldServer", "Saving data!\n");
if (entity != nullptr && entity->GetCharacter() != nullptr) {
auto* skillComponent = entity->GetComponent<SkillComponent>();
auto t = std::chrono::high_resolution_clock::now();
auto ticks = 0;
if (skillComponent != nullptr) {
skillComponent->Reset();
}
std::string message = "Saving character " + entity->GetCharacter()->GetName() + "...\n";
Game::logger->Log("WorldServer", message);
entity->GetCharacter()->SaveXMLToDatabase();
message = "Character data for " + entity->GetCharacter()->GetName() + " was saved!\n";
Game::logger->Log("WorldServer", message);
}
}
Game::logger->Log("WorldServer", "Attempting to shutdown world, zone (%i), instance (%i), max 10 seconds...\n", Game::server->GetZoneID(), instanceID);
if (PropertyManagementComponent::Instance() != nullptr) {
Game::logger->Log("WorldServer", "Saving ALL property data for zone %i clone %i!\n", zoneId, PropertyManagementComponent::Instance()->GetCloneId());
PropertyManagementComponent::Instance()->Save();
Game::logger->Log("WorldServer", "ALL property data saved for zone %i clone %i!\n", zoneId, PropertyManagementComponent::Instance()->GetCloneId());
}
while (true)
{
if (worldShutdownSequenceStarted)
{
break;
}
Game::logger->Log("WorldServer", "ALL DATA HAS BEEN SAVED FOR ZONE %i INSTANCE %i!\n", zoneId, instanceID);
t += std::chrono::milliseconds(highFrameRate);
std::this_thread::sleep_until(t);
while (Game::server->GetReplicaManager()->GetParticipantCount() > 0) {
const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(0);
ticks++;
if (ticks == 600)
{
break;
}
}
Game::server->Disconnect(player, SERVER_DISCON_KICK);
}
SendShutdownMessageToMaster();
}
void WorldShutdownSequence() {
if (worldShutdownSequenceStarted || worldShutdownSequenceComplete) {
return;
}
worldShutdownSequenceStarted = true;
Game::logger->Log("WorldServer", "Zone (%i) instance (%i) shutting down outside of main loop!\n", Game::server->GetZoneID(), instanceID);
WorldShutdownProcess(Game::server->GetZoneID());
FinalizeShutdown();
}
void FinalizeShutdown() {
//Delete our objects here:
if (Game::physicsWorld) Game::physicsWorld = nullptr;
if (Game::zoneManager) delete Game::zoneManager;
Game::logger->Log("WorldServer", "Shutdown complete, zone (%i), instance (%i)\n", Game::server->GetZoneID(), instanceID);
Metrics::Clear();
Database::Destroy("WorldServer");
delete Game::chatFilter;
delete Game::server;
delete Game::logger;
worldShutdownSequenceComplete = true;
exit(EXIT_SUCCESS);
}
void SendShutdownMessageToMaster() {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_SHUTDOWN_RESPONSE);
Game::server->SendToMaster(&bitStream);
}