[Loot] Add content filtering to lootdrop_entries (#4229)

* [Loot] Add content filtering to lootdrop_entries

* Comment unnecessary

* We have two sections of this code for some reason

* Comments

* Fix versions
This commit is contained in:
Chris Miles 2024-04-15 05:26:38 -05:00 committed by GitHub
parent 0a3f1d3c41
commit 0d09edf9aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 157 additions and 58 deletions

View File

@ -5480,6 +5480,20 @@ ADD INDEX `level_skill_cap`(`skill_id`, `class_id`, `level`, `cap`);
ALTER TABLE `account` ALTER TABLE `account`
ADD COLUMN `auto_login_charname` varchar(64) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `charname`; ADD COLUMN `auto_login_charname` varchar(64) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `charname`;
)" )"
},
ManifestEntry{
.version = 9270,
.description = "2024_04_31_content_flagging_lootdrop_entries.sql",
.check = "SHOW COLUMNS FROM `lootdrop_entries` LIKE 'content_flags'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `lootdrop_entries` ADD `min_expansion` tinyint(4) NOT NULL DEFAULT -1;
ALTER TABLE `lootdrop_entries` ADD `max_expansion` tinyint(4) NOT NULL DEFAULT -1;
ALTER TABLE `lootdrop_entries` ADD `content_flags` varchar(100) NULL;
ALTER TABLE `lootdrop_entries` ADD `content_flags_disabled` varchar(100) NULL;
)",
.content_schema_update = true
} }
// -- template; copy/paste this when you need to create a new entry // -- template; copy/paste this when you need to create a new entry
// ManifestEntry{ // ManifestEntry{

View File

@ -19,17 +19,21 @@
class BaseLootdropEntriesRepository { class BaseLootdropEntriesRepository {
public: public:
struct LootdropEntries { struct LootdropEntries {
uint32_t lootdrop_id; uint32_t lootdrop_id;
int32_t item_id; int32_t item_id;
uint16_t item_charges; uint16_t item_charges;
uint8_t equip_item; uint8_t equip_item;
float chance; float chance;
float disabled_chance; float disabled_chance;
uint16_t trivial_min_level; uint16_t trivial_min_level;
uint16_t trivial_max_level; uint16_t trivial_max_level;
uint8_t multiplier; uint8_t multiplier;
uint16_t npc_min_level; uint16_t npc_min_level;
uint16_t npc_max_level; uint16_t npc_max_level;
int8_t min_expansion;
int8_t max_expansion;
std::string content_flags;
std::string content_flags_disabled;
}; };
static std::string PrimaryKey() static std::string PrimaryKey()
@ -51,6 +55,10 @@ public:
"multiplier", "multiplier",
"npc_min_level", "npc_min_level",
"npc_max_level", "npc_max_level",
"min_expansion",
"max_expansion",
"content_flags",
"content_flags_disabled",
}; };
} }
@ -68,6 +76,10 @@ public:
"multiplier", "multiplier",
"npc_min_level", "npc_min_level",
"npc_max_level", "npc_max_level",
"min_expansion",
"max_expansion",
"content_flags",
"content_flags_disabled",
}; };
} }
@ -108,17 +120,21 @@ public:
{ {
LootdropEntries e{}; LootdropEntries e{};
e.lootdrop_id = 0; e.lootdrop_id = 0;
e.item_id = 0; e.item_id = 0;
e.item_charges = 1; e.item_charges = 1;
e.equip_item = 0; e.equip_item = 0;
e.chance = 1; e.chance = 1;
e.disabled_chance = 0; e.disabled_chance = 0;
e.trivial_min_level = 0; e.trivial_min_level = 0;
e.trivial_max_level = 0; e.trivial_max_level = 0;
e.multiplier = 1; e.multiplier = 1;
e.npc_min_level = 0; e.npc_min_level = 0;
e.npc_max_level = 0; e.npc_max_level = 0;
e.min_expansion = -1;
e.max_expansion = -1;
e.content_flags = "";
e.content_flags_disabled = "";
return e; return e;
} }
@ -155,17 +171,21 @@ public:
if (results.RowCount() == 1) { if (results.RowCount() == 1) {
LootdropEntries e{}; LootdropEntries e{};
e.lootdrop_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0; e.lootdrop_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0; e.item_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.item_charges = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 1; e.item_charges = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 1;
e.equip_item = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0; e.equip_item = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.chance = row[4] ? strtof(row[4], nullptr) : 1; e.chance = row[4] ? strtof(row[4], nullptr) : 1;
e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0;
e.trivial_min_level = row[6] ? static_cast<uint16_t>(strtoul(row[6], nullptr, 10)) : 0; e.trivial_min_level = row[6] ? static_cast<uint16_t>(strtoul(row[6], nullptr, 10)) : 0;
e.trivial_max_level = row[7] ? static_cast<uint16_t>(strtoul(row[7], nullptr, 10)) : 0; e.trivial_max_level = row[7] ? static_cast<uint16_t>(strtoul(row[7], nullptr, 10)) : 0;
e.multiplier = row[8] ? static_cast<uint8_t>(strtoul(row[8], nullptr, 10)) : 1; e.multiplier = row[8] ? static_cast<uint8_t>(strtoul(row[8], nullptr, 10)) : 1;
e.npc_min_level = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0; e.npc_min_level = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0;
e.npc_max_level = row[10] ? static_cast<uint16_t>(strtoul(row[10], nullptr, 10)) : 0; e.npc_max_level = row[10] ? static_cast<uint16_t>(strtoul(row[10], nullptr, 10)) : 0;
e.min_expansion = row[11] ? static_cast<int8_t>(atoi(row[11])) : -1;
e.max_expansion = row[12] ? static_cast<int8_t>(atoi(row[12])) : -1;
e.content_flags = row[13] ? row[13] : "";
e.content_flags_disabled = row[14] ? row[14] : "";
return e; return e;
} }
@ -210,6 +230,10 @@ public:
v.push_back(columns[8] + " = " + std::to_string(e.multiplier)); v.push_back(columns[8] + " = " + std::to_string(e.multiplier));
v.push_back(columns[9] + " = " + std::to_string(e.npc_min_level)); v.push_back(columns[9] + " = " + std::to_string(e.npc_min_level));
v.push_back(columns[10] + " = " + std::to_string(e.npc_max_level)); v.push_back(columns[10] + " = " + std::to_string(e.npc_max_level));
v.push_back(columns[11] + " = " + std::to_string(e.min_expansion));
v.push_back(columns[12] + " = " + std::to_string(e.max_expansion));
v.push_back(columns[13] + " = '" + Strings::Escape(e.content_flags) + "'");
v.push_back(columns[14] + " = '" + Strings::Escape(e.content_flags_disabled) + "'");
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@ -242,6 +266,10 @@ public:
v.push_back(std::to_string(e.multiplier)); v.push_back(std::to_string(e.multiplier));
v.push_back(std::to_string(e.npc_min_level)); v.push_back(std::to_string(e.npc_min_level));
v.push_back(std::to_string(e.npc_max_level)); v.push_back(std::to_string(e.npc_max_level));
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@ -282,6 +310,10 @@ public:
v.push_back(std::to_string(e.multiplier)); v.push_back(std::to_string(e.multiplier));
v.push_back(std::to_string(e.npc_min_level)); v.push_back(std::to_string(e.npc_min_level));
v.push_back(std::to_string(e.npc_max_level)); v.push_back(std::to_string(e.npc_max_level));
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@ -315,17 +347,21 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
LootdropEntries e{}; LootdropEntries e{};
e.lootdrop_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0; e.lootdrop_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0; e.item_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.item_charges = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 1; e.item_charges = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 1;
e.equip_item = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0; e.equip_item = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.chance = row[4] ? strtof(row[4], nullptr) : 1; e.chance = row[4] ? strtof(row[4], nullptr) : 1;
e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0;
e.trivial_min_level = row[6] ? static_cast<uint16_t>(strtoul(row[6], nullptr, 10)) : 0; e.trivial_min_level = row[6] ? static_cast<uint16_t>(strtoul(row[6], nullptr, 10)) : 0;
e.trivial_max_level = row[7] ? static_cast<uint16_t>(strtoul(row[7], nullptr, 10)) : 0; e.trivial_max_level = row[7] ? static_cast<uint16_t>(strtoul(row[7], nullptr, 10)) : 0;
e.multiplier = row[8] ? static_cast<uint8_t>(strtoul(row[8], nullptr, 10)) : 1; e.multiplier = row[8] ? static_cast<uint8_t>(strtoul(row[8], nullptr, 10)) : 1;
e.npc_min_level = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0; e.npc_min_level = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0;
e.npc_max_level = row[10] ? static_cast<uint16_t>(strtoul(row[10], nullptr, 10)) : 0; e.npc_max_level = row[10] ? static_cast<uint16_t>(strtoul(row[10], nullptr, 10)) : 0;
e.min_expansion = row[11] ? static_cast<int8_t>(atoi(row[11])) : -1;
e.max_expansion = row[12] ? static_cast<int8_t>(atoi(row[12])) : -1;
e.content_flags = row[13] ? row[13] : "";
e.content_flags_disabled = row[14] ? row[14] : "";
all_entries.push_back(e); all_entries.push_back(e);
} }
@ -350,17 +386,21 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
LootdropEntries e{}; LootdropEntries e{};
e.lootdrop_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0; e.lootdrop_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0; e.item_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.item_charges = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 1; e.item_charges = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 1;
e.equip_item = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0; e.equip_item = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.chance = row[4] ? strtof(row[4], nullptr) : 1; e.chance = row[4] ? strtof(row[4], nullptr) : 1;
e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0;
e.trivial_min_level = row[6] ? static_cast<uint16_t>(strtoul(row[6], nullptr, 10)) : 0; e.trivial_min_level = row[6] ? static_cast<uint16_t>(strtoul(row[6], nullptr, 10)) : 0;
e.trivial_max_level = row[7] ? static_cast<uint16_t>(strtoul(row[7], nullptr, 10)) : 0; e.trivial_max_level = row[7] ? static_cast<uint16_t>(strtoul(row[7], nullptr, 10)) : 0;
e.multiplier = row[8] ? static_cast<uint8_t>(strtoul(row[8], nullptr, 10)) : 1; e.multiplier = row[8] ? static_cast<uint8_t>(strtoul(row[8], nullptr, 10)) : 1;
e.npc_min_level = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0; e.npc_min_level = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0;
e.npc_max_level = row[10] ? static_cast<uint16_t>(strtoul(row[10], nullptr, 10)) : 0; e.npc_max_level = row[10] ? static_cast<uint16_t>(strtoul(row[10], nullptr, 10)) : 0;
e.min_expansion = row[11] ? static_cast<int8_t>(atoi(row[11])) : -1;
e.max_expansion = row[12] ? static_cast<int8_t>(atoi(row[12])) : -1;
e.content_flags = row[13] ? row[13] : "";
e.content_flags_disabled = row[14] ? row[14] : "";
all_entries.push_back(e); all_entries.push_back(e);
} }
@ -446,6 +486,10 @@ public:
v.push_back(std::to_string(e.multiplier)); v.push_back(std::to_string(e.multiplier));
v.push_back(std::to_string(e.npc_min_level)); v.push_back(std::to_string(e.npc_min_level));
v.push_back(std::to_string(e.npc_max_level)); v.push_back(std::to_string(e.npc_max_level));
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@ -479,6 +523,10 @@ public:
v.push_back(std::to_string(e.multiplier)); v.push_back(std::to_string(e.multiplier));
v.push_back(std::to_string(e.npc_min_level)); v.push_back(std::to_string(e.npc_min_level));
v.push_back(std::to_string(e.npc_max_level)); v.push_back(std::to_string(e.npc_max_level));
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }

View File

@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9269 #define CURRENT_BINARY_DATABASE_VERSION 9270
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043
#endif #endif

View File

@ -22,7 +22,7 @@ void NPC::AddLootTable(uint32 loottable_id, bool is_global)
if (!npctype_id) { if (!npctype_id) {
return; return;
} }
if (!is_global) { if (!is_global) {
m_loot_copper = 0; m_loot_copper = 0;
m_loot_silver = 0; m_loot_silver = 0;
@ -145,11 +145,30 @@ void NPC::AddLootDropTable(uint32 lootdrop_id, uint8 drop_limit, uint8 min_drop)
"-- NPC [{}] Lootdrop [{}] Item [{}] ({}_ Chance [{}] Multiplier [{}]", "-- NPC [{}] Lootdrop [{}] Item [{}] ({}_ Chance [{}] Multiplier [{}]",
GetCleanName(), GetCleanName(),
lootdrop_id, lootdrop_id,
database.GetItem(e.item_id)->Name, database.GetItem(e.item_id) ? database.GetItem(e.item_id)->Name : "Unknown",
e.item_id, e.item_id,
e.chance, e.chance,
e.multiplier e.multiplier
); );
if (!content_service.DoesPassContentFiltering(
ContentFlags{
.min_expansion = e.min_expansion,
.max_expansion = e.max_expansion,
.content_flags = e.content_flags,
.content_flags_disabled = e.content_flags_disabled
}
)) {
LogLoot(
"-- NPC [{}] Lootdrop [{}] Item [{}] ({}) does not pass content filtering",
GetCleanName(),
lootdrop_id,
e.item_id,
database.GetItem(e.item_id) ? database.GetItem(e.item_id)->Name : "Unknown"
);
continue;
}
for (int j = 0; j < e.multiplier; ++j) { for (int j = 0; j < e.multiplier; ++j) {
if (zone->random.Real(0.0, 100.0) <= e.chance && MeetsLootDropLevelRequirements(e, true)) { if (zone->random.Real(0.0, 100.0) <= e.chance && MeetsLootDropLevelRequirements(e, true)) {
const EQ::ItemData *database_item = database.GetItem(e.item_id); const EQ::ItemData *database_item = database.GetItem(e.item_id);
@ -215,6 +234,24 @@ void NPC::AddLootDropTable(uint32 lootdrop_id, uint8 drop_limit, uint8 min_drop)
if (drops < min_drop || roll_table_chance_bypass || (float) zone->random.Real(0.0, 1.0) >= no_loot_prob) { if (drops < min_drop || roll_table_chance_bypass || (float) zone->random.Real(0.0, 1.0) >= no_loot_prob) {
float roll = (float) zone->random.Real(0.0, roll_t); float roll = (float) zone->random.Real(0.0, roll_t);
for (const auto &e: le) { for (const auto &e: le) {
if (!content_service.DoesPassContentFiltering(
ContentFlags{
.min_expansion = e.min_expansion,
.max_expansion = e.max_expansion,
.content_flags = e.content_flags,
.content_flags_disabled = e.content_flags_disabled
}
)) {
LogLoot(
"-- NPC [{}] Lootdrop [{}] Item [{}] ({}) does not pass content filtering",
GetCleanName(),
lootdrop_id,
e.item_id,
database.GetItem(e.item_id) ? database.GetItem(e.item_id)->Name : "Unknown"
);
continue;
}
const auto *db_item = database.GetItem(e.item_id); const auto *db_item = database.GetItem(e.item_id);
if (db_item) { if (db_item) {
// if it doesn't meet the requirements do nothing // if it doesn't meet the requirements do nothing