From 28c5b326247b4764351bf997bcb246f22a202a35 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 26 Aug 2016 18:01:34 -0400 Subject: [PATCH 1/2] Modified quantity in Merchant_Sell_Struct to be uint32 in accordance with Merchant_Purchase_Struct. This will allow you to buy stacks of items beyond 255. (Tested with a stack of 1,000 Arrows.) --- common/eq_packet_structs.h | 3 +-- common/patches/sod_structs.h | 3 +-- common/patches/sof_structs.h | 3 +-- common/patches/titanium_structs.h | 3 +-- common/patches/uf_structs.h | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index cf411ef72..fec5af412 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -1934,8 +1934,7 @@ struct Merchant_Sell_Struct { /*004*/ uint32 playerid; // Player's entity id /*008*/ uint32 itemslot; uint32 unknown12; -/*016*/ uint8 quantity; // Already sold -/*017*/ uint8 Unknown016[3]; +/*016*/ uint32 quantity; /*020*/ uint32 price; }; struct Merchant_Purchase_Struct { diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 6a73b7283..bd428c653 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -1880,8 +1880,7 @@ struct Merchant_Sell_Struct { /*004*/ uint32 playerid; // Player's entity id /*008*/ uint32 itemslot; /*012*/ uint32 unknown12; -/*016*/ uint8 quantity; // Already sold -/*017*/ uint8 Unknown017[3]; +/*016*/ uint32 quantity; /*020*/ uint32 Unknown020; /*024*/ uint32 price; /*028*/ uint32 pricehighorderbits; // It appears the price is 64 bits in SoD+ diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 0256132bd..3c0861f42 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -1859,8 +1859,7 @@ struct Merchant_Sell_Struct { /*004*/ uint32 playerid; // Player's entity id /*008*/ uint32 itemslot; uint32 unknown12; -/*016*/ uint8 quantity; // Already sold -/*017*/ uint8 Unknown016[3]; +/*016*/ uint32 quantity; /*020*/ uint32 price; }; struct Merchant_Purchase_Struct { diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 8d258871f..c6a5e6a49 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -1657,8 +1657,7 @@ struct Merchant_Sell_Struct { /*004*/ uint32 playerid; // Player's entity id /*008*/ uint32 itemslot; uint32 unknown12; -/*016*/ uint8 quantity; // Already sold -/*017*/ uint8 Unknown016[3]; +/*016*/ uint32 quantity; /*020*/ uint32 price; }; struct Merchant_Purchase_Struct { diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 46e5b21cb..1ba5616dd 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -1921,8 +1921,7 @@ struct Merchant_Sell_Struct { /*004*/ uint32 playerid; // Player's entity id /*008*/ uint32 itemslot; /*012*/ uint32 unknown12; -/*016*/ uint8 quantity; // Already sold -/*017*/ uint8 Unknown017[3]; +/*016*/ uint32 quantity; /*020*/ uint32 Unknown020; /*024*/ uint32 price; /*028*/ uint32 pricehighorderbits; // It appears the price is 64 bits in Underfoot+ From ed9b6db3695c66e51b122c5e9f02e91414aaf397 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Sat, 27 Aug 2016 17:22:56 -0400 Subject: [PATCH 2/2] Added optional IP-based account exemptions. --- changelog.txt | 6 + common/database.cpp | 12 ++ common/database.h | 2 + common/eqemu_logsys.h | 4 +- common/ruletypes.h | 1 + common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../git/required/2016_08_27_ip_exemptions.sql | 14 +++ world/client.cpp | 2 +- world/clientlist.cpp | 104 +++++++++--------- 10 files changed, 92 insertions(+), 56 deletions(-) create mode 100644 utils/sql/git/required/2016_08_27_ip_exemptions.sql diff --git a/changelog.txt b/changelog.txt index cde46e646..2aa32e8ac 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 08/27/2016 == +Kinglykrab: Added optional IP-based account exemptions. + - To use this system simply set World:EnableIPExemptions to true and create an entry in the ip_exemptions table. + - Example: exemption_ip of IP 192.168.1.4 in ip_exemptions with exemption_amount of 1 will allow only 1 account to login from IP 192.168.1.4. + - Note: If there is no exemption, the amount of accounts logged in from a singular IP will default to World:MaxClientsPerIP + == 08/23/2016 == noudess: Force mobs on a depop @ end pathgrid to still do this on idle zones. This makes them be more random after a zone is idle, rather than always showing diff --git a/common/database.cpp b/common/database.cpp index 6ecbb75dd..a4c862ae0 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2136,3 +2136,15 @@ bool Database::SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year return results.Success(); } + +int Database::GetIPExemption(std::string account_ip) { + std::string query = StringFormat("SELECT `exemption_amount` FROM `ip_exemptions` WHERE `exemption_ip` = '%s'", account_ip.c_str()); + auto results = QueryDatabase(query); + + if (results.Success() && results.RowCount() > 0) { + auto row = results.begin(); + return atoi(row[0]); + } + + return RuleI(World, MaxClientsPerIP); +} \ No newline at end of file diff --git a/common/database.h b/common/database.h index ed08484b1..547e741e5 100644 --- a/common/database.h +++ b/common/database.h @@ -186,6 +186,8 @@ public: void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus); void SetAgreementFlag(uint32 acctid); + + int GetIPExemption(std::string account_ip); /* Groups */ diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 153d61582..bed33253c 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -83,6 +83,7 @@ namespace Logs { Server_Client_Packet_With_Dump, Client_Server_Packet_With_Dump, Login_Server, + Client_Login, MaxCategoryID /* Don't Remove this*/ }; @@ -131,7 +132,8 @@ namespace Logs { "Packet :: Client -> Server Unhandled", "Packet :: Server -> Client (Dump)", "Packet :: Client -> Server (Dump)", - "Login Server" + "Login Server", + "Client Login" }; } diff --git a/common/ruletypes.h b/common/ruletypes.h index c19ebee45..855afe3e0 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -205,6 +205,7 @@ RULE_INT(World, ExemptMaxClientsStatus, -1) // Exempt accounts from the MaxClien RULE_INT(World, AddMaxClientsPerIP, -1) // Maximum number of clients allowed to connect per IP address if account status is < ExemptMaxClientsStatus. Default value: -1 (feature disabled) RULE_INT(World, AddMaxClientsStatus, -1) // Accounts with status >= this rule will be allowed to use the amount of accounts defined in the AddMaxClientsPerIP. Default value: -1 (feature disabled) RULE_BOOL(World, MaxClientsSetByStatus, false) // If True, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP +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) diff --git a/common/version.h b/common/version.h index f06a68c05..0fa1f2090 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9098 +#define CURRENT_BINARY_DATABASE_VERSION 9099 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9008 #else diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 4030395c3..1f5beec28 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -352,6 +352,7 @@ 9096|2016_03_05_secondary_recall.sql|SHOW COLUMNS FROM `character_bind` LIKE 'slot'|empty| 9097|2016_07_03_npc_class_as_last_name.sql|SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'NPC:UseClassAsLastName'|empty| 9098|2016_08_26_object_size_tilt.sql|SHOW COLUMNS FROM `object` LIKE 'size'|empty| +9099|2016_08_27_ip_exemptions.sql|SHOW TABLES LIKE 'ip_exemptions'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2016_08_27_ip_exemptions.sql b/utils/sql/git/required/2016_08_27_ip_exemptions.sql new file mode 100644 index 000000000..9aa8582b0 --- /dev/null +++ b/utils/sql/git/required/2016_08_27_ip_exemptions.sql @@ -0,0 +1,14 @@ +-- IP Exemptions table structure +DROP TABLE IF EXISTS `ip_exemptions`; +CREATE TABLE `ip_exemptions` ( + `exemption_id` int(11) NOT NULL AUTO_INCREMENT, + `exemption_ip` varchar(255) DEFAULT NULL, + `exemption_amount` int(11) DEFAULT NULL, + PRIMARY KEY (`exemption_id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; + +-- Rule Value Entry, Default to false +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES ('1', 'World:EnableIPExemptions', 'true', 'notation'); + +-- Logging Category Entry +INSERT INTO `logsys_categories` (`log_category_id`, `log_category_description`, `log_to_console`, `log_to_file`, `log_to_gmsay`) VALUES ('44', 'Client Login', '1', '1', '1'); \ No newline at end of file diff --git a/world/client.cpp b/world/client.cpp index d2ca3705a..8d036827c 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -722,7 +722,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { return true; } - if (RuleI(World, MaxClientsPerIP) >= 0) { + if (RuleB(World, EnableIPExemptions) || RuleI(World, MaxClientsPerIP) >= 0) { client_list.GetCLEIP(this->GetIP()); //Check current CLE Entry IPs against incoming connection } diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 7c83701f4..fd92051b3 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -29,6 +29,7 @@ #include "../common/classes.h" #include "../common/packet_dump.h" #include "wguild_mgr.h" +#include "../common/misc.h" #include @@ -142,7 +143,6 @@ void ClientList::EnforceSessionLimit(uint32 iLSAccountID) { //Check current CLE Entry IPs against incoming connection void ClientList::GetCLEIP(uint32 iIP) { - ClientListEntry* countCLEIPs = 0; LinkedListIterator iterator(clientlist); @@ -150,64 +150,62 @@ void ClientList::GetCLEIP(uint32 iIP) { iterator.Reset(); while(iterator.MoreElements()) { - - countCLEIPs = iterator.GetData(); - - // If the IP matches, and the connection admin status is below the exempt status, - // or exempt status is less than 0 (no-one is exempt) - if ((countCLEIPs->GetIP() == iIP) && - ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || - (RuleI(World, ExemptMaxClientsStatus) < 0))) { - - // Increment the occurences of this IP address - IPInstances++; - - // If the number of connections exceeds the lower limit - if (IPInstances > (RuleI(World, MaxClientsPerIP))) { - - // If MaxClientsSetByStatus is set to True, override other IP Limit Rules - if (RuleB(World, MaxClientsSetByStatus)) { - - // The IP Limit is set by the status of the account if status > MaxClientsPerIP - if (IPInstances > countCLEIPs->Admin()) { - - if(RuleB(World, IPLimitDisconnectAll)) { + countCLEIPs = iterator.GetData(); + if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0))) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) + IPInstances++; // Increment the occurences of this IP address + Log.Out(Logs::General, Logs::Client_Login, "Account ID: %i Account Name: %s IP: %s.", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + if (RuleB(World, EnableIPExemptions)) { + Log.Out(Logs::General, Logs::Client_Login, "Account ID: %i Account Name: %s IP: %s IP Instances: %i Max IP Instances: %i", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str())); + if (IPInstances > database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str())) { + if(RuleB(World, IPLimitDisconnectAll)) { + Log.Out(Logs::General, Logs::Client_Login, "Disconnect: All accounts on IP %s", long2ip(countCLEIPs->GetIP()).c_str()); + DisconnectByIP(iIP); + return; + } else { + Log.Out(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + countCLEIPs->SetOnline(CLE_Status_Offline); + iterator.RemoveCurrent(); + continue; + } + } + } else { + if (IPInstances > (RuleI(World, MaxClientsPerIP))) { // If the number of connections exceeds the lower limit + if (RuleB(World, MaxClientsSetByStatus)) { // If MaxClientsSetByStatus is set to True, override other IP Limit Rules + Log.Out(Logs::General, Logs::Client_Login, "Account ID: %i Account Name: %s IP: %s IP Instances: %i Max IP Instances: %i", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, countCLEIPs->Admin()); + if (IPInstances > countCLEIPs->Admin()) { // The IP Limit is set by the status of the account if status > MaxClientsPerIP + if(RuleB(World, IPLimitDisconnectAll)) { + Log.Out(Logs::General, Logs::Client_Login, "Disconnect: All accounts on IP %s", long2ip(countCLEIPs->GetIP()).c_str()); + DisconnectByIP(iIP); + return; + } else { + Log.Out(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + countCLEIPs->SetOnline(CLE_Status_Offline); // Remove the connection + iterator.RemoveCurrent(); + continue; + } + } + } else if ((countCLEIPs->Admin() < RuleI(World, AddMaxClientsStatus)) || (RuleI(World, AddMaxClientsStatus) < 0)) { // Else if the Admin status of the connection is not eligible for the higher limit, or there is no higher limit (AddMaxClientStatus < 0) + if(RuleB(World, IPLimitDisconnectAll)) { + Log.Out(Logs::General, Logs::Client_Login, "Disconnect: All accounts on IP %s", long2ip(countCLEIPs->GetIP()).c_str()); DisconnectByIP(iIP); return; } else { - // Remove the connection - countCLEIPs->SetOnline(CLE_Status_Offline); + Log.Out(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + countCLEIPs->SetOnline(CLE_Status_Offline); // Remove the connection + iterator.RemoveCurrent(); + continue; + } + } else if (IPInstances > RuleI(World, AddMaxClientsPerIP)) { // else they are eligible for the higher limit, but if they exceed that + if(RuleB(World, IPLimitDisconnectAll)) { + Log.Out(Logs::General, Logs::Client_Login, "Disconnect: All accounts on IP %s", long2ip(countCLEIPs->GetIP()).c_str()); + DisconnectByIP(iIP); + return; + } else { + Log.Out(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + countCLEIPs->SetOnline(CLE_Status_Offline); // Remove the connection iterator.RemoveCurrent(); continue; } - } - } - // Else if the Admin status of the connection is not eligible for the higher limit, - // or there is no higher limit (AddMaxClientStatus<0) - else if ((countCLEIPs->Admin() < (RuleI(World, AddMaxClientsStatus)) || - (RuleI(World, AddMaxClientsStatus) < 0))) { - - if(RuleB(World, IPLimitDisconnectAll)) { - DisconnectByIP(iIP); - return; - } else { - // Remove the connection - countCLEIPs->SetOnline(CLE_Status_Offline); - iterator.RemoveCurrent(); - continue; - } - } - // else they are eligible for the higher limit, but if they exceed that - else if (IPInstances > RuleI(World, AddMaxClientsPerIP)) { - - if(RuleB(World, IPLimitDisconnectAll)) { - DisconnectByIP(iIP); - return; - } else { - // Remove the connection - countCLEIPs->SetOnline(CLE_Status_Offline); - iterator.RemoveCurrent(); - continue; } } }