Merge pull request #499 from EmosewaMC/master-server-shutdown

Dramatically speed up server shutdown and specify database destruction source in logging message
This commit is contained in:
David Markowitz 2022-04-10 17:25:04 -07:00 committed by GitHub
commit f45cb380e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 115 additions and 114 deletions

View File

@ -57,7 +57,7 @@ int main(int argc, char** argv) {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
} catch (sql::SQLException& ex) { } catch (sql::SQLException& ex) {
Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s\n", ex.what()); Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s\n", ex.what());
Database::Destroy(); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
return 0; return 0;
@ -143,11 +143,12 @@ int main(int argc, char** argv) {
} }
//Delete our objects here: //Delete our objects here:
Database::Destroy(); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
return 0; exit(EXIT_SUCCESS);
return EXIT_SUCCESS;
} }
dLogger * SetupLogger() { dLogger * SetupLogger() {

View File

@ -61,7 +61,7 @@ int main(int argc, char** argv) {
} }
catch (sql::SQLException& ex) { catch (sql::SQLException& ex) {
Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s\n", ex.what()); Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s\n", ex.what());
Database::Destroy(); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
return 0; return 0;
@ -150,11 +150,12 @@ int main(int argc, char** argv) {
} }
//Delete our objects here: //Delete our objects here:
Database::Destroy(); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
return 0; exit(EXIT_SUCCESS);
return EXIT_SUCCESS;
} }
dLogger * SetupLogger() { dLogger * SetupLogger() {

View File

@ -26,9 +26,10 @@ void Database::Connect(const string& host, const string& database, const string&
con->setClientOption("MYSQL_OPT_RECONNECT", &myTrue); con->setClientOption("MYSQL_OPT_RECONNECT", &myTrue);
} //Connect } //Connect
void Database::Destroy() { void Database::Destroy(std::string source) {
if (!con) return; if (!con) return;
Game::logger->Log("Database", "Destroying MySQL connection!\n"); if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!\n", source.c_str());
else Game::logger->Log("Database", "Destroying MySQL connection!\n");
con->close(); con->close();
delete con; delete con;
} //Destroy } //Destroy

View File

@ -22,7 +22,7 @@ private:
public: public:
static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password); static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
static void Destroy(); static void Destroy(std::string source="");
static sql::Statement* CreateStmt(); static sql::Statement* CreateStmt();
static sql::PreparedStatement* CreatePreppedStmt(const std::string& query); static sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
}; };

View File

@ -161,6 +161,8 @@ public:
*/ */
const std::map<LWOOBJID, LWOOBJID>& GetModels() const; const std::map<LWOOBJID, LWOOBJID>& GetModels() const;
LWOCLONEID GetCloneId() { return clone_Id; };
private: private:
/** /**
* This * This

View File

@ -47,6 +47,7 @@ namespace Game {
bool shutdownSequenceStarted = false; bool shutdownSequenceStarted = false;
void ShutdownSequence(); void ShutdownSequence();
int FinalizeShutdown();
dLogger* SetupLogger(); dLogger* SetupLogger();
void StartAuthServer(); void StartAuthServer();
void StartChatServer(); void StartChatServer();
@ -168,7 +169,7 @@ int main(int argc, char** argv) {
std::cout << "Account created successfully!\n"; std::cout << "Account created successfully!\n";
Database::Destroy(); Database::Destroy("MasterServer");
delete Game::logger; delete Game::logger;
return EXIT_SUCCESS; return EXIT_SUCCESS;
@ -318,13 +319,8 @@ int main(int argc, char** argv) {
t += std::chrono::milliseconds(highFrameRate); t += std::chrono::milliseconds(highFrameRate);
std::this_thread::sleep_until(t); std::this_thread::sleep_until(t);
} }
FinalizeShutdown();
//Delete our objects here: exit(EXIT_SUCCESS);
Database::Destroy();
delete Game::im;
delete Game::server;
delete Game::logger;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@ -664,7 +660,7 @@ void HandlePacket(Packet* packet) {
RakNet::BitStream inStream(packet->data, packet->length, false); RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header); uint64_t header = inStream.Read(header);
auto* instance =Game::im->GetInstanceBySysAddr(packet->systemAddress); auto* instance = Game::im->GetInstanceBySysAddr(packet->systemAddress);
if (instance == nullptr) { if (instance == nullptr) {
return; return;
@ -745,12 +741,20 @@ void ShutdownSequence() {
auto ticks = 0; auto ticks = 0;
if (!Game::im) { if (!Game::im) {
exit(0); exit(EXIT_SUCCESS);
} }
Game::logger->Log("MasterServer", "Attempting to shutdown instances, max 60 seconds...\n"); Game::logger->Log("MasterServer", "Attempting to shutdown instances, max 60 seconds...\n");
while (true) { while (true) {
auto packet = Game::server->Receive();
if (packet) {
HandlePacket(packet);
Game::server->DeallocatePacket(packet);
packet = nullptr;
}
auto done = true; auto done = true;
for (auto* instance : Game::im->GetInstances()) { for (auto* instance : Game::im->GetInstances()) {
@ -764,6 +768,7 @@ void ShutdownSequence() {
} }
if (done) { if (done) {
Game::logger->Log("MasterServer", "Finished shutting down MasterServer!\n");
break; break;
} }
@ -773,9 +778,21 @@ void ShutdownSequence() {
ticks++; ticks++;
if (ticks == 600 * 6) { if (ticks == 600 * 6) {
Game::logger->Log("MasterServer", "Finished shutting down by timeout!\n");
break; break;
} }
} }
exit(0); FinalizeShutdown();
} }
int FinalizeShutdown() {
//Delete our objects here:
Database::Destroy("MasterServer");
delete Game::im;
delete Game::server;
delete Game::logger;
exit(EXIT_SUCCESS);
return EXIT_SUCCESS;
}

View File

@ -77,6 +77,9 @@ bool chatConnected = false;
bool worldShutdownSequenceStarted = false; bool worldShutdownSequenceStarted = false;
bool worldShutdownSequenceComplete = false; bool worldShutdownSequenceComplete = false;
void WorldShutdownSequence(); void WorldShutdownSequence();
void WorldShutdownProcess(uint32_t zoneId);
void FinalizeShutdown();
void SendShutdownMessageToMaster();
dLogger* SetupLogger(int zoneID, int instanceID); dLogger* SetupLogger(int zoneID, int instanceID);
void HandlePacketChat(Packet* packet); void HandlePacketChat(Packet* packet);
@ -475,80 +478,16 @@ int main(int argc, char** argv) {
} }
} }
if (worldShutdownSequenceStarted && !worldShutdownSequenceComplete) if (worldShutdownSequenceStarted && !worldShutdownSequenceComplete) {
{ WorldShutdownProcess(zoneID);
if (framesSinceShutdownSequence == 0) { break;
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;
}
} }
Metrics::AddMeasurement(MetricVariable::CPUTime, (1e6 * (1000.0 * (std::clock() - metricCPUTimeStart))) / CLOCKS_PER_SEC); Metrics::AddMeasurement(MetricVariable::CPUTime, (1e6 * (1000.0 * (std::clock() - metricCPUTimeStart))) / CLOCKS_PER_SEC);
Metrics::EndMeasurement(MetricVariable::Frame); Metrics::EndMeasurement(MetricVariable::Frame);
} }
FinalizeShutdown();
//Delete our objects here: return EXIT_SUCCESS;
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);
} }
dLogger * SetupLogger(int zoneID, int instanceID) { dLogger * SetupLogger(int zoneID, int instanceID) {
@ -1279,35 +1218,75 @@ void HandlePacket(Packet* packet) {
} }
} }
void WorldShutdownSequence() void WorldShutdownProcess(uint32_t zoneId) {
{ Game::logger->Log("WorldServer", "Saving map %i instance %i\n", zoneId, instanceID);
if (worldShutdownSequenceStarted || worldShutdownSequenceComplete) for (auto i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i) {
{ const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i);
return;
}
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(); if (skillComponent != nullptr) {
auto ticks = 0; 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) Game::logger->Log("WorldServer", "ALL DATA HAS BEEN SAVED FOR ZONE %i INSTANCE %i!\n", zoneId, instanceID);
{
if (worldShutdownSequenceStarted)
{
break;
}
t += std::chrono::milliseconds(highFrameRate); while (Game::server->GetReplicaManager()->GetParticipantCount() > 0) {
std::this_thread::sleep_until(t); const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(0);
ticks++; Game::server->Disconnect(player, SERVER_DISCON_KICK);
}
if (ticks == 600) SendShutdownMessageToMaster();
{
break;
}
}
} }
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);
}