From e547a1e778fe548a2c851ff6de22d820e4406d29 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 26 Feb 2018 20:02:27 -0500 Subject: [PATCH] Added 'server ready' broadcast to UCS server so clients will reconnect after crash --- common/ruletypes.h | 1 + common/servertalk.h | 6 ++++++ common/shareddb.cpp | 17 +++++++++++++++ common/shareddb.h | 1 + ucs/ucs.cpp | 6 ++++-- ucs/worldserver.cpp | 36 +++++++++++++++++++++++++++++++ ucs/worldserver.h | 5 +++++ world/ucs.cpp | 1 + world/worlddb.cpp | 17 --------------- world/worlddb.h | 1 - zone/worldserver.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 122 insertions(+), 20 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index ea220a511..ed51f4474 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -615,6 +615,7 @@ RULE_INT(Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able t RULE_INT(Chat, GlobalChatLevelLimit, 8) //level limit you need to of reached to talk in ooc/auction/chat if your karma is too low. RULE_INT(Chat, ExpireClientVersionRequests, 3) // time in seconds to keep current cv requests active RULE_INT(Chat, ExpireClientVersionReplies, 30) // time in seconds to keep current cv replies active +RULE_INT(Chat, UCSBroadcastServerReadyDelay, 60) // time in seconds to delay broadcast `server ready` after start-up RULE_CATEGORY_END() RULE_CATEGORY(Merchant) diff --git a/common/servertalk.h b/common/servertalk.h index 36e973195..82f39319f 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -192,6 +192,7 @@ #define ServerOP_CZSetEntityVariableByClientName 0x4012 #define ServerOP_UCSClientVersionRequest 0x4013 #define ServerOP_UCSClientVersionReply 0x4014 +#define ServerOP_UCSBroadcastServerReady 0x4015 /* Query Server OP Codes */ #define ServerOP_QSPlayerLogTrades 0x5010 #define ServerOP_QSPlayerLogHandins 0x5011 @@ -1289,6 +1290,11 @@ struct UCSClientVersionReply_Struct { EQEmu::versions::ClientVersion client_version; }; +struct UCSBroadcastServerReady_Struct { + char chat_prefix[128]; + char mail_prefix[128]; +}; + #pragma pack() #endif diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 09c936425..4a82c32da 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -110,6 +110,23 @@ uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { return EntitledTime; } +void SharedDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) +{ + char MailKeyString[17]; + + if (RuleB(Chat, EnableMailKeyIPVerification) == true) + sprintf(MailKeyString, "%08X%08X", IPAddress, MailKey); + else + sprintf(MailKeyString, "%08X", MailKey); + + std::string query = StringFormat("UPDATE character_data SET mailkey = '%s' WHERE id = '%i'", + MailKeyString, CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + Log(Logs::General, Logs::Error, "SharedDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); + +} + bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) { // Delete cursor items diff --git a/common/shareddb.h b/common/shareddb.h index d3d8020a1..171c2b8c6 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -72,6 +72,7 @@ class SharedDatabase : public Database void SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message); bool GetCommandSettings(std::map>> &command_settings); uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID); + void SetMailKey(int CharID, int IPAddress, int MailKey); /* Character InventoryProfile diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index 1fbfec995..b59effd03 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -140,10 +140,12 @@ int main() { worldserver = new WorldServer; - // now that we can send packets to world, see if there's a - // broadcast opcode that tells the client to relog into ucs + worldserver->ActivateBroadcastServerReadyTimer(); while(RunLoops) { + // this triggers clients to 'reconnect' to ucs after server crash + if (worldserver->HasBroadcastServerReadyTimer()) + worldserver->ProcessBroadcastServerReady(); Timer::SetCurrentTime(); diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index b5251b64c..556be152f 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -45,6 +45,8 @@ WorldServer::WorldServer() { m_connection.reset(new EQ::Net::ServertalkClient(Config->WorldIP, Config->WorldTCPPort, false, "UCS", Config->SharedKey)); m_connection->OnMessage(std::bind(&WorldServer::ProcessMessage, this, std::placeholders::_1, std::placeholders::_2)); + + m_bsr_timer = nullptr; } WorldServer::~WorldServer() @@ -134,3 +136,37 @@ void WorldServer::ProcessClientVersionRequests(std::list& id_list) { } id_list.clear(); } + +void WorldServer::ProcessBroadcastServerReady() { + if (m_bsr_timer && (*m_bsr_timer) <= Timer::GetCurrentTime()) { + UCSBroadcastServerReady_Struct bsr; + memset(&bsr, 0, sizeof(UCSBroadcastServerReady_Struct)); + + sprintf(bsr.chat_prefix, "%s,%i,%s.", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str() + ); + sprintf(bsr.mail_prefix, "%s,%i,%s.", + Config->ChatHost.c_str(), + Config->MailPort, + Config->ShortName.c_str() + ); + + EQ::Net::DynamicPacket dp_bsr; + dp_bsr.PutData(0, &bsr, sizeof(UCSBroadcastServerReady_Struct)); + m_connection->Send(ServerOP_UCSBroadcastServerReady, dp_bsr); + + safe_delete(m_bsr_timer); + } +} + +void WorldServer::ActivateBroadcastServerReadyTimer() { + safe_delete(m_bsr_timer); + m_bsr_timer = new uint32; + + // clients do not drop their connection to ucs immediately... + // it can take upwards of 60 seconds to process the drop + // and clients will not re-connect to ucs until that occurs + *m_bsr_timer = (Timer::GetCurrentTime() + (RuleI(Chat, UCSBroadcastServerReadyDelay) * 1000)); +} diff --git a/ucs/worldserver.h b/ucs/worldserver.h index 53f52823a..66ed0ec32 100644 --- a/ucs/worldserver.h +++ b/ucs/worldserver.h @@ -30,8 +30,13 @@ public: void ProcessMessage(uint16 opcode, EQ::Net::Packet &); void ProcessClientVersionRequests(std::list& id_list); + + void ProcessBroadcastServerReady(); + bool HasBroadcastServerReadyTimer() { return (m_bsr_timer != nullptr); } + void ActivateBroadcastServerReadyTimer(); private: + uint32* m_bsr_timer; std::unique_ptr m_connection; }; diff --git a/world/ucs.cpp b/world/ucs.cpp index dfa8e116a..76cba7e4b 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -52,6 +52,7 @@ void UCSConnection::ProcessPacket(uint16 opcode, EQ::Net::Packet &p) Log(Logs::Detail, Logs::UCS_Server, "Got authentication from UCS when they are already authenticated."); break; } + case ServerOP_UCSBroadcastServerReady: case ServerOP_UCSClientVersionRequest: { zoneserver_list.SendPacket(pack); diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 0b118ff2e..cbf901cf7 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -516,23 +516,6 @@ void WorldDatabase::GetLauncherList(std::vector &rl) { } -void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) -{ - char MailKeyString[17]; - - if(RuleB(Chat, EnableMailKeyIPVerification) == true) - sprintf(MailKeyString, "%08X%08X", IPAddress, MailKey); - else - sprintf(MailKeyString, "%08X", MailKey); - - std::string query = StringFormat("UPDATE character_data SET mailkey = '%s' WHERE id = '%i'", - MailKeyString, CharID); - auto results = QueryDatabase(query); - if (!results.Success()) - Log(Logs::General, Logs::Error, "WorldDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); - -} - bool WorldDatabase::GetCharacterLevel(const char *name, int &level) { std::string query = StringFormat("SELECT level FROM character_data WHERE name = '%s'", name); diff --git a/world/worlddb.h b/world/worlddb.h index b0c2ff221..2afd69920 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -34,7 +34,6 @@ public: int MoveCharacterToBind(int CharID, uint8 bindnum = 0); void GetLauncherList(std::vector &result); - void SetMailKey(int CharID, int IPAddress, int MailKey); bool GetCharacterLevel(const char *name, int &level); bool LoadCharacterCreateAllocations(); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 1f771d131..4ad2267b3 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1813,6 +1813,57 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } + case ServerOP_UCSBroadcastServerReady: + { + UCSBroadcastServerReady_Struct* bsr = (UCSBroadcastServerReady_Struct*)pack->pBuffer; + EQApplicationPacket* outapp = nullptr; + std::string buffer; + + for (auto liter : entity_list.GetClientList()) { + auto c = liter.second; + if (!c) + continue; + + int MailKey = zone->random.Int(1, INT_MAX); + + database.SetMailKey(c->CharacterID(), c->GetIP(), MailKey); + + char ConnectionType; + + // chat server packet + if (c->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) + ConnectionType = 'U'; + else if (c->ClientVersionBit() & EQEmu::versions::bit_SoFAndLater) + ConnectionType = 'S'; + else + ConnectionType = 'C'; + + buffer = bsr->chat_prefix; + buffer.append(StringFormat("%s,%c%08X", c->GetName(), ConnectionType, MailKey)); + + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + + // mail server packet + if (c->ClientVersionBit() & EQEmu::versions::bit_TitaniumAndEarlier) + ConnectionType = 'M'; + + buffer = bsr->mail_prefix; + buffer.append(StringFormat("%s,%c%08X", c->GetName(), ConnectionType, MailKey)); + + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + } + break; + } case ServerOP_UCSClientVersionRequest: { UCSClientVersionRequest_Struct* cvreq = (UCSClientVersionRequest_Struct*)pack->pBuffer;