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