From 6bdc9b6ba52a30daa1e9a0964370d8d04219f7ed Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 13 Jul 2019 16:16:15 -0700 Subject: [PATCH] Add support for single account login to both world and login server, should also work with eqemu login server, can be turned off in rules if you don't mind the side effects double logins cause. Also lowered the linkdead time to 30s + 90s default (2min) --- CMakeLists.txt | 6 +- common/net/daybreak_connection.h | 4 +- common/ruletypes.h | 5 +- common/servertalk.h | 9 +++ loginserver/world_server.cpp | 17 +++-- .../optional/2019_07_13_linkdead_changes.sql | 2 + world/clientlist.cpp | 65 +++++------------ world/clientlist.h | 2 +- world/login_server.cpp | 72 +++++++++++-------- 9 files changed, 89 insertions(+), 93 deletions(-) create mode 100644 utils/sql/git/optional/2019_07_13_linkdead_changes.sql diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b0b36639..be92c7d0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,14 +290,14 @@ ADD_DEFINITIONS(-DGLM_FORCE_CTOR_INIT) ADD_DEFINITIONS(-DGLM_ENABLE_EXPERIMENTAL) #Find everything we need -FIND_PACKAGE(ZLIB REQUIRED) +FIND_PACKAGE(ZLIB) FIND_PACKAGE(MySQL REQUIRED) IF(EQEMU_BUILD_PERL) FIND_PACKAGE(PerlLibs REQUIRED) INCLUDE_DIRECTORIES(SYSTEM "${PERL_INCLUDE_PATH}") ENDIF(EQEMU_BUILD_PERL) -SET(SERVER_LIBS common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY} uv_a fmt RecastNavigation::Detour) +SET(SERVER_LIBS common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} uv_a fmt RecastNavigation::Detour) FIND_PACKAGE(Sodium REQUIRED) IF(SODIUM_FOUND) @@ -320,7 +320,7 @@ IF(ZLIB_FOUND) SET(SERVER_LIBS ${SERVER_LIBS} ${ZLIB_LIBRARY}) ENDIF() ELSE() - INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/zlibng") + INCLUDE_DIRECTORIES(BEFORE SYSTEM "${CMAKE_CURRENT_BINARY_DIR}/libs/zlibng" "${CMAKE_CURRENT_SOURCE_DIR}/libs/zlibng") SET(SERVER_LIBS ${SERVER_LIBS} "zlibstatic") ENDIF() diff --git a/common/net/daybreak_connection.h b/common/net/daybreak_connection.h index 4d2f62e27..b4f3ca41c 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 = 90000; + stale_connection_ms = 30000; connect_stale_ms = 5000; crc_length = 2; max_packet_size = 512; @@ -269,7 +269,7 @@ namespace EQ simulated_in_packet_loss = 0; simulated_out_packet_loss = 0; tic_rate_hertz = 60.0; - resend_timeout = 90000; + resend_timeout = 30000; connection_close_time = 2000; outgoing_data_rate = 0.0; } diff --git a/common/ruletypes.h b/common/ruletypes.h index 3c9e5d677..ccbef9339 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -225,8 +225,6 @@ RULE_BOOL(World, MaxClientsSetByStatus, false) // If True, IP Limiting will be s RULE_BOOL(World, EnableIPExemptions, false) // If True, ip_exemptions table is used, if there is no entry for the IP it will default to RuleI(World, MaxClientsPerIP) RULE_BOOL(World, ClearTempMerchantlist, true) // Clears temp merchant items when world boots. RULE_BOOL(World, DeleteStaleCorpeBackups, true) // Deletes stale corpse backups older than 2 weeks. -RULE_INT(World, AccountSessionLimit, -1) //Max number of characters allowed on at once from a single account (-1 is disabled) -RULE_INT(World, ExemptAccountLimitStatus, -1) //Min status required to be exempt from multi-session per account limiting (-1 is disabled) RULE_BOOL(World, GMAccountIPList, false) // Check ip list against GM Accounts, AntiHack GM Accounts. RULE_INT(World, MinGMAntiHackStatus, 1) //Minimum GM status to check against AntiHack list RULE_INT(World, SoFStartZoneID, -1) //Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled) @@ -241,10 +239,11 @@ RULE_BOOL (World, IPLimitDisconnectAll, false) RULE_BOOL(World, MaxClientsSimplifiedLogic, false) // New logic that only uses ExemptMaxClientsStatus and MaxClientsPerIP. Done on the loginserver. This mimics the P99-style special IP rules. RULE_INT (World, TellQueueSize, 20) RULE_BOOL(World, StartZoneSameAsBindOnCreation, true) //Should the start zone ALWAYS be the same location as your bind? +RULE_BOOL(World, DisallowDuplicateAccountLogins, true) RULE_CATEGORY_END() RULE_CATEGORY(Zone) -RULE_INT(Zone, ClientLinkdeadMS, 180000) //the time a client remains link dead on the server after a sudden disconnection +RULE_INT(Zone, ClientLinkdeadMS, 90000) //the time a client remains link dead on the server after a sudden disconnection RULE_INT(Zone, GraveyardTimeMS, 1200000) //ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone RULE_BOOL(Zone, EnableShadowrest, 1) // enables or disables the shadowrest zone feature for player corpses. Default is turned on. RULE_BOOL(Zone, UsePlayerCorpseBackups, true) // Keeps backups of player corpses. diff --git a/common/servertalk.h b/common/servertalk.h index b942eb547..c5cd149d7 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -213,6 +213,15 @@ enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_ #define ServerOP_Speech 0x4513 +enum { + UserToWorldStatusWorldUnavail = 0, + UserToWorldStatusSuccess = 1, + UserToWorldStatusSuspended = -1, + UserToWorldStatusBanned = -2, + UserToWorldStatusWorldAtCapacity = -3, + UserToWorldStatusAlreadyOnline = -4 +}; + /************ PACKET RELATED STRUCT ************/ class ServerPacket { diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index 55fc03b7b..d953ccc3a 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -165,21 +165,26 @@ void WorldServer::ProcessUsertoWorldResp(uint16_t opcode, const EQ::Net::Packet switch (utwr->response) { - case 1: + case UserToWorldStatusSuccess: per->Message = 101; break; - case 0: + case UserToWorldStatusWorldUnavail: per->Message = 326; break; - case -1: + case UserToWorldStatusSuspended: per->Message = 337; break; - case -2: + case UserToWorldStatusBanned: per->Message = 338; break; - case -3: - per->Message = 303; + case UserToWorldStatusWorldAtCapacity: + per->Message = 339; break; + case UserToWorldStatusAlreadyOnline: + per->Message = 111; + break; + default: + per->Message = 102; } if (server.options.IsTraceOn()) diff --git a/utils/sql/git/optional/2019_07_13_linkdead_changes.sql b/utils/sql/git/optional/2019_07_13_linkdead_changes.sql new file mode 100644 index 000000000..6bc6bde50 --- /dev/null +++ b/utils/sql/git/optional/2019_07_13_linkdead_changes.sql @@ -0,0 +1,2 @@ +UPDATE `rule_values` SET `rule_value`='90000' WHERE `rule_name`='Zone:ClientLinkdeadMS'; +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'DisallowDuplicateAccountLogins', 'true', 'Requires account logins to be unique.'); diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 96dd45076..1264957a7 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -99,52 +99,6 @@ ClientListEntry* ClientList::GetCLE(uint32 iID) { return 0; } -//Account Limiting Code to limit the number of characters allowed on from a single account at once. -void ClientList::EnforceSessionLimit(uint32 iLSAccountID) { - - ClientListEntry* ClientEntry = 0; - - LinkedListIterator iterator(clientlist, BACKWARD); - - int CharacterCount = 0; - - iterator.Reset(); - - while(iterator.MoreElements()) { - - ClientEntry = iterator.GetData(); - - if ((ClientEntry->LSAccountID() == iLSAccountID) && - ((ClientEntry->Admin() <= (RuleI(World, ExemptAccountLimitStatus))) || (RuleI(World, ExemptAccountLimitStatus) < 0))) { - - CharacterCount++; - - if (CharacterCount >= (RuleI(World, AccountSessionLimit))){ - // If we have a char name, they are in a zone, so send a kick to the zone server - if(strlen(ClientEntry->name())) { - - auto pack = - new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, "SessionLimit"); - strcpy(skp->name, ClientEntry->name()); - skp->adminrank = 255; - zoneserver_list.SendPacket(pack); - safe_delete(pack); - } - - ClientEntry->SetOnline(CLE_Status_Offline); - - iterator.RemoveCurrent(); - - continue; - } - } - iterator.Advance(); - } -} - - //Check current CLE Entry IPs against incoming connection void ClientList::GetCLEIP(uint32 iIP) { @@ -272,7 +226,7 @@ ClientListEntry* ClientList::FindCharacter(const char* name) { } iterator.Advance(); } - return 0; + return nullptr; } ClientListEntry* ClientList::FindCLEByAccountID(uint32 iAccID) { @@ -285,7 +239,7 @@ ClientListEntry* ClientList::FindCLEByAccountID(uint32 iAccID) { } iterator.Advance(); } - return 0; + return nullptr; } ClientListEntry* ClientList::FindCLEByCharacterID(uint32 iCharID) { @@ -298,7 +252,20 @@ ClientListEntry* ClientList::FindCLEByCharacterID(uint32 iCharID) { } iterator.Advance(); } - return 0; + return nullptr; +} + +ClientListEntry* ClientList::FindCLEByLSID(uint32 iLSID) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while (iterator.MoreElements()) { + if (iterator.GetData()->LSID() == iLSID) { + return iterator.GetData(); + } + iterator.Advance(); + } + return nullptr; } void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnection* connection, const char* iName) { diff --git a/world/clientlist.h b/world/clientlist.h index 3ca6291c2..5b3bad297 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -57,11 +57,11 @@ public: ClientListEntry* FindCharacter(const char* name); ClientListEntry* FindCLEByAccountID(uint32 iAccID); ClientListEntry* FindCLEByCharacterID(uint32 iCharID); + ClientListEntry* FindCLEByLSID(uint32 iLSID); ClientListEntry* GetCLE(uint32 iID); void GetCLEIP(uint32 iIP); uint32 GetCLEIPCount(uint32 iLSAccountID); void DisconnectByIP(uint32 iIP); - void EnforceSessionLimit(uint32 iLSAccountID); void CLCheckStale(); void CLEKeepAlive(uint32 numupdates, uint32* wid); void CLEAdd(uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); diff --git a/world/login_server.cpp b/world/login_server.cpp index b642e77ad..61cc53bc2 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -35,6 +35,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "worlddb.h" #include "zonelist.h" #include "clientlist.h" +#include "cliententry.h" #include "world_config.h" extern ZSList zoneserver_list; @@ -65,38 +66,58 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) { uint32 id = database.GetAccountIDFromLSID(utwr->lsaccountid); int16 status = database.CheckStatus(id); - auto outpack = new ServerPacket; - outpack->opcode = ServerOP_UsertoWorldResp; - outpack->size = sizeof(UsertoWorldResponse_Struct); - outpack->pBuffer = new uchar[outpack->size]; - memset(outpack->pBuffer, 0, outpack->size); - UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*)outpack->pBuffer; + ServerPacket outpack; + outpack.opcode = ServerOP_UsertoWorldResp; + outpack.size = sizeof(UsertoWorldResponse_Struct); + outpack.pBuffer = new uchar[outpack.size]; + memset(outpack.pBuffer, 0, outpack.size); + UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*)outpack.pBuffer; utwrs->lsaccountid = utwr->lsaccountid; utwrs->ToID = utwr->FromID; + utwrs->worldid = utwr->worldid; + utwrs->response = UserToWorldStatusSuccess; if (Config->Locked == true) { - if ((status == 0 || status < 100) && (status != -2 || status != -1)) - utwrs->response = 0; - if (status >= 100) - utwrs->response = 1; - } - else { - utwrs->response = 1; + if (status < 100) { + utwrs->response = UserToWorldStatusWorldUnavail; + SendPacket(&outpack); + return; + } } int32 x = Config->MaxClients; - if ((int32)numplayers >= x && x != -1 && x != 255 && status < 80) - utwrs->response = -3; + if ((int32)numplayers >= x && x != -1 && x != 255 && status < 80) { + utwrs->response = UserToWorldStatusWorldAtCapacity; + SendPacket(&outpack); + return; + } - if (status == -1) - utwrs->response = -1; - if (status == -2) - utwrs->response = -2; + if (status == -1) { + utwrs->response = UserToWorldStatusSuspended; + SendPacket(&outpack); + return; + } - utwrs->worldid = utwr->worldid; - SendPacket(outpack); - delete outpack; + if (status == -2) { + utwrs->response = UserToWorldStatusBanned; + SendPacket(&outpack); + return; + } + + if (RuleB(World, DisallowDuplicateAccountLogins)) { + auto cle = client_list.FindCLEByLSID(utwr->lsaccountid); + if (cle != nullptr) { + auto status = cle->GetOnline(); + if (CLE_Status_Never != status && CLE_Status_Offline != status) { + utwrs->response = UserToWorldStatusAlreadyOnline; + SendPacket(&outpack); + return; + } + } + } + + SendPacket(&outpack); } void LoginServer::ProcessLSClientAuth(uint16_t opcode, EQ::Net::Packet &p) { @@ -105,13 +126,6 @@ void LoginServer::ProcessLSClientAuth(uint16_t opcode, EQ::Net::Packet &p) { try { auto slsca = p.GetSerialize(0); - - if (RuleI(World, AccountSessionLimit) >= 0) { - // Enforce the limit on the number of characters on the same account that can be - // online at the same time. - client_list.EnforceSessionLimit(slsca.lsaccount_id); - } - client_list.CLEAdd(slsca.lsaccount_id, slsca.name, slsca.key, slsca.worldadmin, slsca.ip, slsca.local); } catch (std::exception &ex) {