Added 'server ready' broadcast to UCS server so clients will reconnect after crash

This commit is contained in:
Uleat 2018-02-26 20:02:27 -05:00
parent c469571f62
commit e547a1e778
11 changed files with 122 additions and 20 deletions

View File

@ -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)

View File

@ -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

View File

@ -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<EQEmu::ItemInstance*>::const_iterator &start, std::list<EQEmu::ItemInstance*>::const_iterator &end)
{
// Delete cursor items

View File

@ -72,6 +72,7 @@ class SharedDatabase : public Database
void SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message);
bool GetCommandSettings(std::map<std::string, std::pair<uint8, std::vector<std::string>>> &command_settings);
uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID);
void SetMailKey(int CharID, int IPAddress, int MailKey);
/*
Character InventoryProfile

View File

@ -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();

View File

@ -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<uint32>& 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));
}

View File

@ -30,8 +30,13 @@ public:
void ProcessMessage(uint16 opcode, EQ::Net::Packet &);
void ProcessClientVersionRequests(std::list<uint32>& id_list);
void ProcessBroadcastServerReady();
bool HasBroadcastServerReadyTimer() { return (m_bsr_timer != nullptr); }
void ActivateBroadcastServerReadyTimer();
private:
uint32* m_bsr_timer;
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
};

View File

@ -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);

View File

@ -516,23 +516,6 @@ void WorldDatabase::GetLauncherList(std::vector<std::string> &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);

View File

@ -34,7 +34,6 @@ public:
int MoveCharacterToBind(int CharID, uint8 bindnum = 0);
void GetLauncherList(std::vector<std::string> &result);
void SetMailKey(int CharID, int IPAddress, int MailKey);
bool GetCharacterLevel(const char *name, int &level);
bool LoadCharacterCreateAllocations();

View File

@ -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;