diff --git a/common/net/daybreak_connection.h b/common/net/daybreak_connection.h index b4f3ca41c..883ecefac 100644 --- a/common/net/daybreak_connection.h +++ b/common/net/daybreak_connection.h @@ -257,7 +257,7 @@ namespace EQ resend_delay_min = 150; resend_delay_max = 5000; connect_delay_ms = 500; - stale_connection_ms = 30000; + stale_connection_ms = 60000; connect_stale_ms = 5000; crc_length = 2; max_packet_size = 512; diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index a433705e6..26ef62b82 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -471,7 +471,7 @@ static void ProcessCommandIgnore(Client *c, std::string Ignoree) { Clientlist::Clientlist(int ChatPort) { EQStreamManagerInterfaceOptions chat_opts(ChatPort, false, false); chat_opts.opcode_size = 1; - chat_opts.daybreak_options.stale_connection_ms = 300000; + chat_opts.daybreak_options.stale_connection_ms = 600000; chat_opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS); chat_opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor); chat_opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS); diff --git a/world/client.cpp b/world/client.cpp index 022bf0d41..c29b7d698 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -104,6 +104,7 @@ Client::Client(EQStreamInterface* ieqs) char_name[0] = 0; charid = 0; zone_waiting_for_bootup = 0; + enter_world_triggered = false; StartInTutorial = false; m_ClientVersion = eqs->ClientVersion(); @@ -1118,6 +1119,7 @@ bool Client::Process() { Log(Logs::General, Logs::World_Server, "Zone bootup timer expired, bootup failed or too slow."); TellClientZoneUnavailable(); } + if(connect.Check()){ SendGuildList();// Send OPCode: OP_GuildsList SendApproveWorld(); @@ -1189,13 +1191,18 @@ void Client::EnterWorld(bool TryBootup) { else zone_server = zoneserver_list.FindByZoneID(zone_id); - //Tell all the zones to drop any client with this lsid because we're coming back in. - zoneserver_list.DropClient(GetLSID()); - const char *zone_name = database.GetZoneName(zone_id, true); if (zone_server) { - // warn the world we're comming, so it knows not to shutdown - zone_server->IncomingClient(this); + if (false == enter_world_triggered) { + //Drop any clients we own in other zones. + zoneserver_list.DropClient(GetLSID(), zone_server); + + // warn the zone we're coming + zone_server->IncomingClient(this); + + //tell the server not to trigger this multiple times before we get a zone unavailable + enter_world_triggered = true; + } } else { if (TryBootup) { @@ -1214,9 +1221,17 @@ void Client::EnterWorld(bool TryBootup) { return; } } + zone_waiting_for_bootup = 0; - if(!cle) { + if (GetAdmin() < 80 && zoneserver_list.IsZoneLocked(zone_id)) { + Log(Logs::General, Logs::World_Server, "Enter world failed. Zone is locked."); + TellClientZoneUnavailable(); + return; + } + + if (!cle) { + TellClientZoneUnavailable(); return; } @@ -1233,12 +1248,6 @@ void Client::EnterWorld(bool TryBootup) { ); if (seen_character_select) { - if (GetAdmin() < 80 && zoneserver_list.IsZoneLocked(zone_id)) { - Log(Logs::General, Logs::World_Server, "Enter world failed. Zone is locked."); - TellClientZoneUnavailable(); - return; - } - auto pack = new ServerPacket; pack->opcode = ServerOP_AcceptWorldEntrance; pack->size = sizeof(WorldToZone_Struct); @@ -1358,6 +1367,7 @@ void Client::TellClientZoneUnavailable() { zone_id = 0; zone_waiting_for_bootup = 0; + enter_world_triggered = false; autobootup_timeout.Disable(); } diff --git a/world/client.h b/world/client.h index e7ab52fa3..4dbad85c5 100644 --- a/world/client.h +++ b/world/client.h @@ -82,6 +82,7 @@ private: bool is_player_zoning; Timer autobootup_timeout; uint32 zone_waiting_for_bootup; + bool enter_world_triggered; bool StartInTutorial; EQEmu::versions::ClientVersion m_ClientVersion; diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 1d06071c4..77782ee9b 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -708,11 +708,16 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval) } } -void ZSList::DropClient(uint32 lsid) { +void ZSList::DropClient(uint32 lsid, ZoneServer *ignore_zoneserver) { ServerPacket packet(ServerOP_DropClient, sizeof(ServerZoneDropClient_Struct)); auto drop = (ServerZoneDropClient_Struct*)packet.pBuffer; drop->lsid = lsid; - SendPacket(&packet); + + for (auto &zs : zone_server_list) { + if (zs.get() != ignore_zoneserver) { + zs->SendPacket(&packet); + } + } } void ZSList::OnTick(EQ::Timer *t) diff --git a/world/zonelist.h b/world/zonelist.h index b99a88e09..8b8525f57 100644 --- a/world/zonelist.h +++ b/world/zonelist.h @@ -57,7 +57,7 @@ public: void SOPZoneBootup(const char *adminname, uint32 ZoneServerID, const char *zonename, bool iMakeStatic = false); void UpdateUCSServerAvailable(bool ucss_available = true); void WorldShutDown(uint32 time, uint32 interval); - void DropClient(uint32 lsid); + void DropClient(uint32 lsid, ZoneServer *ignore_zoneserver); ZoneServer* FindByPort(uint16 port); ZoneServer* FindByID(uint32 ZoneID); diff --git a/zone/aa.cpp b/zone/aa.cpp index 48ee715f7..4d9854d86 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -489,7 +489,7 @@ void Client::ResetAA() { database.DeleteCharacterLeadershipAAs(CharacterID()); // undefined for these clients if (ClientVersionBit() & EQEmu::versions::maskTitaniumAndEarlier) - Kick(); + Kick("AA Reset on client that doesn't support it"); } void Client::SendClearAA() diff --git a/zone/client.cpp b/zone/client.cpp index 3f0e33d2f..7e6baf180 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -846,7 +846,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s { if(AttemptedMessages > RuleI(Chat, MaxMessagesBeforeKick)) { - Kick(); + Kick("Sent too many chat messages at once."); return; } if(GlobalChatLimiterTimer) @@ -2587,13 +2587,19 @@ void Client::SetPVP(bool toggle, bool message) { Save(); } +void Client::Kick(const std::string &reason) { + client_state = CLIENT_KICKED; + + Log(Logs::General, Logs::Client_Login, "Client [%s] kicked, reason [%s]", GetCleanName(), reason.c_str()); +} + void Client::WorldKick() { auto outapp = new EQApplicationPacket(OP_GMKick, sizeof(GMKick_Struct)); GMKick_Struct* gmk = (GMKick_Struct *)outapp->pBuffer; strcpy(gmk->name,GetName()); QueuePacket(outapp); safe_delete(outapp); - Kick(); + Kick("World kick issued"); } void Client::GMKill() { diff --git a/zone/client.h b/zone/client.h index 2ea27b108..29632d667 100644 --- a/zone/client.h +++ b/zone/client.h @@ -371,9 +371,9 @@ public: inline bool ClientDataLoaded() const { return client_data_loaded; } inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); } - inline void Kick() { client_state = CLIENT_KICKED; } inline void Disconnect() { eqs->Close(); client_state = DISCONNECTED; } inline bool IsLD() const { return (bool) (client_state == CLIENT_LINKDEAD); } + void Kick(const std::string &reason); void WorldKick(); inline uint8 GetAnon() const { return m_pp.anon; } inline PlayerProfile_Struct& GetPP() { return m_pp; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 39f959677..2bd6ed1ac 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1172,10 +1172,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) Log(Logs::General, Logs::Client_Login, "%s failed zone auth check.", cze->char_name); if (nullptr != client) { client->Save(); - client->Kick(); + client->Kick("Failed auth check"); } - //ret = false; // TODO: Can we tell the client to get lost in a good way - client_state = CLIENT_KICKED; return; } @@ -9803,7 +9801,7 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) casting_spell_id); database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); safe_delete_array(detect); - Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots + Kick("Inventory desync"); // Kick client to prevent client and server from getting out-of-sync inventory slots return; } } @@ -9847,7 +9845,7 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app) { - Kick(); // TODO: lets not desync though + Kick("Unimplemented move multiple items"); // TODO: lets not desync though } void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) diff --git a/zone/command.cpp b/zone/command.cpp index 6dda38f71..d95f07e8a 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1912,7 +1912,7 @@ void command_permaclass(Client *c, const Seperator *sep) Log(Logs::General, Logs::Normal, "Class change request from %s for %s, requested class:%i", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); t->SetBaseClass(atoi(sep->arg[1])); t->Save(); - t->Kick(); + t->Kick("Class was changed."); } } @@ -3895,7 +3895,7 @@ void command_kick(Client *c, const Seperator *sep) client->Message(0, "You have been kicked by %s", c->GetName()); auto outapp = new EQApplicationPacket(OP_GMKick, 0); client->QueuePacket(outapp); - client->Kick(); + client->Kick("Ordered kicked by command"); c->Message(0, "Kick: local: kicking %s", sep->arg[1]); } } @@ -5187,7 +5187,7 @@ void command_name(Client *c, const Seperator *sep) c->Message(0, "Successfully renamed %s to %s", oldname, sep->arg[1]); // until we get the name packet working right this will work c->Message(0, "Sending player to char select."); - target->Kick(); + target->Kick("Name was changed"); } else c->Message(13, "ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", oldname, sep->arg[2]); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index ec4f095b0..96f0fe15b 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1608,7 +1608,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { banker ? banker->GetName() : "UNKNOWN NPC", distance); database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); safe_delete_array(hacked_string); - Kick(); // Kicking player to avoid item loss do to client and server inventories not being sync'd + Kick("Inventory desync"); // Kicking player to avoid item loss do to client and server inventories not being sync'd return false; } } @@ -1822,7 +1822,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Step 4: Check for entity trade if (dst_slot_id >= EQEmu::invslot::TRADE_BEGIN && dst_slot_id <= EQEmu::invslot::TRADE_END) { if (src_slot_id != EQEmu::invslot::slotCursor) { - Kick(); + Kick("Trade with non-cursor item"); return false; } if (with) { diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 9447c48af..c50f6b2f3 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -47,7 +47,7 @@ bool Lua_Client::InZone() { void Lua_Client::Kick() { Lua_Safe_Call_Void(); - self->Kick(); + self->Kick("Lua Quest"); } void Lua_Client::Disconnect() { diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index be1bf2a8e..f86228feb 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -174,7 +174,7 @@ XS(XS_Client_Kick) { if (THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - THIS->Kick(); + THIS->Kick("Perl Quest"); } XSRETURN_EMPTY; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 79f5af8a5..dafc3f1e6 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -809,7 +809,7 @@ void QuestManager::changedeity(int diety_id) { initiator->SetDeity(diety_id); initiator->Message(15,"Your Deity has been changed/set to: %i", diety_id); initiator->Save(1); - initiator->Kick(); + initiator->Kick("Deity change by QuestManager"); } else { @@ -943,7 +943,7 @@ void QuestManager::permaclass(int class_id) { //Makes the client the class specified initiator->SetBaseClass(class_id); initiator->Save(2); - initiator->Kick(); + initiator->Kick("Base class change by QuestManager"); } void QuestManager::permarace(int race_id) { @@ -951,7 +951,7 @@ void QuestManager::permarace(int race_id) { //Makes the client the race specified initiator->SetBaseRace(race_id); initiator->Save(2); - initiator->Kick(); + initiator->Kick("Base race change by QuestManager"); } void QuestManager::permagender(int gender_id) { @@ -959,7 +959,7 @@ void QuestManager::permagender(int gender_id) { //Makes the client the gender specified initiator->SetBaseGender(gender_id); initiator->Save(2); - initiator->Kick(); + initiator->Kick("Base gender change by QuestManager"); } uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { diff --git a/zone/trading.cpp b/zone/trading.cpp index 154fdf148..fe9a72daa 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -112,7 +112,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { // (it just didn't handle partial stack move actions) if (stack_size > 0) { if (!inst->IsStackable() || !inst2 || !inst2->GetItem() || (inst->GetID() != inst2->GetID()) || (stack_size > inst->GetCharges())) { - client->Kick(); + client->Kick("Error stacking item in trade"); return; } @@ -138,7 +138,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { } else { if (inst2 && inst2->GetID()) { - client->Kick(); + client->Kick("Attempting to add null item to trade"); return; } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 864f11448..ffe777808 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -536,6 +536,13 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) SetZoneData(zone->GetZoneID(), zone->GetInstanceID()); if (szic->zoneid == zone->GetZoneID()) { + auto client = entity_list.GetClientByLSID(szic->lsid); + if (client) { + client->Kick("Dropped by world CLE subsystem"); + client->Save(); + } + + zone->RemoveAuth(szic->lsid); zone->AddAuth(szic); // This packet also doubles as "incoming client" notification, lets not shut down before they get here zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); @@ -559,7 +566,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) auto client = entity_list.GetClientByLSID(drop->lsid); if (client) { - client->Kick(); + client->Kick("Dropped by world CLE subsystem"); client->Save(); } }