From 6c2a8edea6e131d937504fc3b0de609cd937f843 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 25 Feb 2018 21:40:45 -0500 Subject: [PATCH] Added ClientVersion request system to UCS server (needed to fix saylinks) --- common/emu_versions.h | 4 +- common/ruletypes.h | 2 + common/servertalk.h | 11 ++++++ ucs/clientlist.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++ ucs/clientlist.h | 18 +++++++++ ucs/ucs.cpp | 5 +++ ucs/worldserver.cpp | 18 +++++++++ ucs/worldserver.h | 2 + world/client.cpp | 8 ++++ world/client.h | 1 + world/ucs.cpp | 8 ++++ world/zoneserver.cpp | 1 + zone/worldserver.cpp | 14 +++++++ 13 files changed, 175 insertions(+), 2 deletions(-) diff --git a/common/emu_versions.h b/common/emu_versions.h index 9d9e1f580..9ab5becde 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -28,7 +28,7 @@ namespace EQEmu { namespace versions { - enum class ClientVersion { + enum class ClientVersion : uint32 { Unknown = 0, Client62, // Build: 'Aug 4 2005 15:40:59' Titanium, // Build: 'Oct 31 2005 10:33:37' @@ -72,7 +72,7 @@ namespace EQEmu uint32 ConvertClientVersionToExpansion(ClientVersion client_version); - enum class MobVersion { + enum class MobVersion : uint32 { Unknown = 0, Client62, Titanium, diff --git a/common/ruletypes.h b/common/ruletypes.h index b6acffc3c..ea220a511 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -613,6 +613,8 @@ RULE_INT(Chat, IntervalDurationMS, 60000) RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000) RULE_INT(Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able to talk in ooc/auction/chat below the level limit 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_CATEGORY_END() RULE_CATEGORY(Merchant) diff --git a/common/servertalk.h b/common/servertalk.h index f5965cd74..36e973195 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -190,6 +190,8 @@ #define ServerOP_ReloadLogs 0x4010 #define ServerOP_ReloadPerlExportSettings 0x4011 #define ServerOP_CZSetEntityVariableByClientName 0x4012 +#define ServerOP_UCSClientVersionRequest 0x4013 +#define ServerOP_UCSClientVersionReply 0x4014 /* Query Server OP Codes */ #define ServerOP_QSPlayerLogTrades 0x5010 #define ServerOP_QSPlayerLogHandins 0x5011 @@ -1278,6 +1280,15 @@ struct ServerRequestTellQueue_Struct { char name[64]; }; +struct UCSClientVersionRequest_Struct { + uint32 character_id; +}; + +struct UCSClientVersionReply_Struct { + uint32 character_id; + EQEmu::versions::ClientVersion client_version; +}; + #pragma pack() #endif diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index 998410ec1..751cf4089 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -512,7 +512,9 @@ Client::Client(std::shared_ptr eqs) { AccountGrabUpdateTimer = new Timer(60000); //check every minute GlobalChatLimiterTimer = new Timer(RuleI(Chat, IntervalDurationMS)); + RawConnectionType = '\0'; TypeOfConnection = ConnectionTypeUnknown; + ClientVersion_ = EQEmu::versions::ClientVersion::Unknown; UnderfootOrLater = false; } @@ -643,6 +645,10 @@ void Clientlist::Process() database.GetAccountStatus((*it)); + // give world packet a chance to arrive and be processed + if ((*it)->GetCharID()) + ClientVersionRequestQueue[(*it)->GetCharID()] = (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionRequests) * 1000)); + if ((*it)->GetConnectionType() == ConnectionTypeCombined) (*it)->SendFriends(); @@ -681,8 +687,35 @@ void Clientlist::Process() it = ClientChatConnections.erase(it); continue; } + + // initiate request if we don't already have a reply from 'world enter' (ucs crash recovery protocol) + if ((*it)->GetClientVersion() == EQEmu::versions::ClientVersion::Unknown) { + if (!CheckForClientVersionReply((*it))) + RequestClientVersion((*it)->GetCharID()); + } + ++it; } + + // delete expired replies + auto repiter = ClientVersionReplyQueue.begin(); + while (repiter != ClientVersionReplyQueue.end()) { + if ((*repiter).second.second <= Timer::GetCurrentTime()) { + repiter = ClientVersionReplyQueue.erase(repiter); + continue; + } + ++repiter; + } + + // delete expired requests + auto reqiter = ClientVersionRequestQueue.begin(); + while (reqiter != ClientVersionRequestQueue.end()) { + if ((*reqiter).second <= Timer::GetCurrentTime()) { + reqiter = ClientVersionRequestQueue.erase(reqiter); + continue; + } + ++reqiter; + } } void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString) @@ -840,6 +873,55 @@ void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString) } } +void Clientlist::RequestClientVersion(uint32 character_id) { + if (!character_id) + return; + + if (ClientVersionRequestQueue.find(character_id) != ClientVersionRequestQueue.end()) { + if (ClientVersionRequestQueue[character_id] > Timer::GetCurrentTime()) + return; + } + + if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { + Log(Logs::Detail, Logs::UCS_Server, "Requesting ClientVersion reply for character id: %u", + character_id); + } + ClientVersionRequestIDs.push_back(character_id); + ClientVersionRequestQueue[character_id] = (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionRequests) * 1000)); +} + +bool Clientlist::QueueClientVersionReply(uint32 character_id, EQEmu::versions::ClientVersion client_version) { + if (!character_id) + return true; + if (client_version == EQEmu::versions::ClientVersion::Unknown) + return false; + + if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { + Log(Logs::Detail, Logs::UCS_Server, "Queueing ClientVersion %u reply for character id: %u", + static_cast(client_version), character_id); + } + ClientVersionReplyQueue[character_id] = cvt_pair(client_version, (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionReplies) * 1000))); + ClientVersionRequestQueue.erase(character_id); + + return true; +} + +bool Clientlist::CheckForClientVersionReply(Client* c) { + if (!c) + return true; + if (ClientVersionReplyQueue.find(c->GetCharID()) == ClientVersionReplyQueue.end()) + return false; + + if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { + Log(Logs::General, Logs::UCS_Server, "Registering ClientVersion %s for stream %s:%u", + EQEmu::versions::ClientVersionName(ClientVersionReplyQueue[c->GetCharID()].first), c->ClientStream->GetRemoteAddr().c_str(), c->ClientStream->GetRemotePort()); + } + c->SetClientVersion(ClientVersionReplyQueue[c->GetCharID()].first); + ClientVersionReplyQueue.erase(c->GetCharID()); + + return true; +} + void Clientlist::CloseAllConnections() { @@ -2132,6 +2214,8 @@ void Client::AccountUpdate() void Client::SetConnectionType(char c) { + RawConnectionType = c; + switch (c) { case 'S': @@ -2161,6 +2245,7 @@ void Client::SetConnectionType(char c) { } default: { + RawConnectionType = '\0'; TypeOfConnection = ConnectionTypeUnknown; Log(Logs::Detail, Logs::UCS_Server, "Connection type is unknown."); } diff --git a/ucs/clientlist.h b/ucs/clientlist.h index 9b72df51a..f84bdc8a2 100644 --- a/ucs/clientlist.h +++ b/ucs/clientlist.h @@ -139,8 +139,13 @@ public: std::string MailBoxName(); int GetMailBoxNumber() { return CurrentMailBox; } int GetMailBoxNumber(std::string CharacterName); + + char GetRawConnectionType() { return RawConnectionType; } void SetConnectionType(char c); ConnectionType GetConnectionType() { return TypeOfConnection; } + void SetClientVersion(EQEmu::versions::ClientVersion client_version) { ClientVersion_ = client_version; } + EQEmu::versions::ClientVersion GetClientVersion() { return ClientVersion_; } + inline bool IsMailConnection() { return (TypeOfConnection == ConnectionTypeMail) || (TypeOfConnection == ConnectionTypeCombined); } void SendNotification(int MailBoxNumber, std::string From, std::string Subject, int MessageID); void ChangeMailBox(int NewMailBox); @@ -167,7 +172,10 @@ private: Timer *GlobalChatLimiterTimer; //60 seconds int AttemptedMessages; bool ForceDisconnect; + + char RawConnectionType; ConnectionType TypeOfConnection; + EQEmu::versions::ClientVersion ClientVersion_; bool UnderfootOrLater; }; @@ -182,12 +190,22 @@ public: Client *IsCharacterOnline(std::string CharacterName); void ProcessOPMailCommand(Client *c, std::string CommandString); + std::list ClientVersionRequestIDs; + + void RequestClientVersion(uint32 character_id); + bool QueueClientVersionReply(uint32 character_id, EQEmu::versions::ClientVersion client_version); + bool CheckForClientVersionReply(Client* c); + private: + typedef std::pair cvt_pair; EQ::Net::EQStreamManager *chatsf; std::list ClientChatConnections; + std::map ClientVersionRequestQueue; + std::map ClientVersionReplyQueue; + OpcodeManager *ChatOpMgr; }; diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index b9e7f41b4..1fbfec995 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -140,12 +140,17 @@ 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 + while(RunLoops) { Timer::SetCurrentTime(); g_Clientlist->Process(); + worldserver->ProcessClientVersionRequests(g_Clientlist->ClientVersionRequestIDs); + if(ChannelListProcessTimer.Check()) ChannelList->Process(); diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index 141211032..b5251b64c 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -114,5 +114,23 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) std::string()); break; } + + case ServerOP_UCSClientVersionReply: + { + UCSClientVersionReply_Struct* cvr = (UCSClientVersionReply_Struct*)pack->pBuffer; + g_Clientlist->QueueClientVersionReply(cvr->character_id, cvr->client_version); + break; + } } } + +void WorldServer::ProcessClientVersionRequests(std::list& id_list) { + UCSClientVersionRequest_Struct cvr; + EQ::Net::DynamicPacket dp_cvr; + for (auto iter : id_list) { + cvr.character_id = iter; + dp_cvr.PutData(0, &cvr, sizeof(cvr)); + m_connection->Send(ServerOP_UCSClientVersionRequest, dp_cvr); + } + id_list.clear(); +} diff --git a/ucs/worldserver.h b/ucs/worldserver.h index aad68e085..53f52823a 100644 --- a/ucs/worldserver.h +++ b/ucs/worldserver.h @@ -29,6 +29,8 @@ public: ~WorldServer(); void ProcessMessage(uint16 opcode, EQ::Net::Packet &); + void ProcessClientVersionRequests(std::list& id_list); + private: std::unique_ptr m_connection; diff --git a/world/client.cpp b/world/client.cpp index aaf7d4c9c..d904e1d2d 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1215,6 +1215,14 @@ void Client::EnterWorld(bool TryBootup) { wtz->response = 0; zone_server->SendPacket(pack); delete pack; + + UCSClientVersionReply_Struct cvr; + cvr.character_id = GetCharID(); + cvr.client_version = GetClientVersion(); + EQ::Net::DynamicPacket dp_cvr; + dp_cvr.PutData(0, &cvr, sizeof(cvr)); + zone_server->HandleMessage(ServerOP_UCSClientVersionReply, dp_cvr); + } else { // if they havent seen character select screen, we can assume this is a zone // to zone movement, which should be preauthorized before they leave the previous zone diff --git a/world/client.h b/world/client.h index 88dc27c57..d36dc692d 100644 --- a/world/client.h +++ b/world/client.h @@ -68,6 +68,7 @@ public: inline const char* GetLSKey() { if (cle) { return cle->GetLSKey(); } return "NOKEY"; } inline uint32 GetCharID() { return charid; } inline const char* GetCharName() { return char_name; } + inline EQEmu::versions::ClientVersion GetClientVersion() { return m_ClientVersion; } inline ClientListEntry* GetCLE() { return cle; } inline void SetCLE(ClientListEntry* iCLE) { cle = iCLE; } private: diff --git a/world/ucs.cpp b/world/ucs.cpp index b745005d4..dfa8e116a 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -2,11 +2,14 @@ #include "../common/eqemu_logsys.h" #include "ucs.h" #include "world_config.h" +#include "zonelist.h" #include "../common/misc_functions.h" #include "../common/md5.h" #include "../common/packet_dump.h" +extern ZSList zoneserver_list; + UCSConnection::UCSConnection() { Stream = 0; @@ -49,6 +52,11 @@ 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_UCSClientVersionRequest: + { + zoneserver_list.SendPacket(pack); + break; + } default: { Log(Logs::Detail, Logs::UCS_Server, "Unknown ServerOPcode from UCS 0x%04x, size %d", opcode, pack->size); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 4de771701..061ee6b72 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1262,6 +1262,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } + case ServerOP_UCSClientVersionReply: case ServerOP_UCSMailMessage: { UCSLink.SendPacket(pack); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 6417b9c1d..1f771d131 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1813,6 +1813,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } + case ServerOP_UCSClientVersionRequest: + { + UCSClientVersionRequest_Struct* cvreq = (UCSClientVersionRequest_Struct*)pack->pBuffer; + Client* c = entity_list.GetClientByCharID(cvreq->character_id); + if (c) { + UCSClientVersionReply_Struct cvrep; + cvrep.character_id = c->CharacterID(); + cvrep.client_version = c->ClientVersion(); + EQ::Net::DynamicPacket dp_cvrep; + dp_cvrep.PutData(0, &cvrep, sizeof(cvrep)); + worldserver.m_connection->Send(ServerOP_UCSClientVersionReply, dp_cvrep); + } + break; + } case ServerOP_CZSetEntityVariableByNPCTypeID: { CZSetEntVarByNPCTypeID_Struct* CZM = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer;