[Tasks] Schema simplification (#2449)

* Combine task_activity item and npc fields

This will make tooling easier.

While denormalizing goallists may not be ideal, it decouples tasks from
rewards which share the table and removes a redundant column in favor
of a using the delimited string which better matches live packet data.

* [Tasks] Deprecate goallists table, migrate reward goal lists, simplify logic

* Update 2022_09_25_task_concat_matchlists.sql

* Update 2022_09_25_task_concat_matchlists.sql

* Tweaks

* Fix reward column name in conversion script

* Task reward stacking

* Update task_client_state.cpp

* Implement stack counts

* Fix reward item instance memory leak

* Validate reward item instance

* Fix item reward message

* Fix findtask

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
hg 2022-09-28 03:31:05 -04:00 committed by GitHub
parent d22fca7593
commit e883703b2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 496 additions and 1111 deletions

View File

@ -207,7 +207,6 @@ SET(repositories
repositories/base/base_friends_repository.h
repositories/base/base_global_loot_repository.h
repositories/base/base_gm_ips_repository.h
repositories/base/base_goallists_repository.h
repositories/base/base_graveyard_repository.h
repositories/base/base_ground_spawns_repository.h
repositories/base/base_group_id_repository.h
@ -384,7 +383,6 @@ SET(repositories
repositories/friends_repository.h
repositories/global_loot_repository.h
repositories/gm_ips_repository.h
repositories/goallists_repository.h
repositories/graveyard_repository.h
repositories/ground_spawns_repository.h
repositories/group_id_repository.h

View File

@ -197,7 +197,6 @@ namespace DatabaseSchema {
"fishing",
"forage",
"global_loot",
"goallists",
"graveyard",
"grid",
"grid_entries",

View File

@ -1,333 +0,0 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
*/
#ifndef EQEMU_BASE_GOALLISTS_REPOSITORY_H
#define EQEMU_BASE_GOALLISTS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseGoallistsRepository {
public:
struct Goallists {
uint32_t listid;
uint32_t entry;
};
static std::string PrimaryKey()
{
return std::string("listid");
}
static std::vector<std::string> Columns()
{
return {
"listid",
"entry",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"listid",
"entry",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("goallists");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static Goallists NewEntity()
{
Goallists e{};
e.listid = 0;
e.entry = 0;
return e;
}
static Goallists GetGoallists(
const std::vector<Goallists> &goallistss,
int goallists_id
)
{
for (auto &goallists : goallistss) {
if (goallists.listid == goallists_id) {
return goallists;
}
}
return NewEntity();
}
static Goallists FindOne(
Database& db,
int goallists_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
BaseSelect(),
goallists_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
Goallists e{};
e.listid = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.entry = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int goallists_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
goallists_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const Goallists &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.listid));
v.push_back(columns[1] + " = " + std::to_string(e.entry));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.listid
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static Goallists InsertOne(
Database& db,
Goallists e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.listid));
v.push_back(std::to_string(e.entry));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.listid = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<Goallists> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.listid));
v.push_back(std::to_string(e.entry));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<Goallists> All(Database& db)
{
std::vector<Goallists> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
Goallists e{};
e.listid = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.entry = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<Goallists> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<Goallists> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
Goallists e{};
e.listid = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.entry = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
};
#endif //EQEMU_BASE_GOALLISTS_REPOSITORY_H

View File

@ -28,11 +28,7 @@ public:
uint32_t goalmethod;
int32_t goalcount;
std::string description_override;
uint32_t npc_id;
uint32_t npc_goal_id;
std::string npc_match_list;
uint32_t item_id;
uint32_t item_goal_id;
std::string item_id_list;
std::string item_list;
int32_t dz_switch_id;
@ -66,11 +62,7 @@ public:
"goalmethod",
"goalcount",
"description_override",
"npc_id",
"npc_goal_id",
"npc_match_list",
"item_id",
"item_goal_id",
"item_id_list",
"item_list",
"dz_switch_id",
@ -100,11 +92,7 @@ public:
"goalmethod",
"goalcount",
"description_override",
"npc_id",
"npc_goal_id",
"npc_match_list",
"item_id",
"item_goal_id",
"item_id_list",
"item_list",
"dz_switch_id",
@ -168,11 +156,7 @@ public:
e.goalmethod = 0;
e.goalcount = 1;
e.description_override = "";
e.npc_id = 0;
e.npc_goal_id = 0;
e.npc_match_list = "";
e.item_id = 0;
e.item_goal_id = 0;
e.item_id_list = "";
e.item_list = "";
e.dz_switch_id = 0;
@ -231,25 +215,21 @@ public:
e.goalmethod = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.goalcount = static_cast<int32_t>(atoi(row[7]));
e.description_override = row[8] ? row[8] : "";
e.npc_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.npc_goal_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.npc_match_list = row[11] ? row[11] : "";
e.item_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
e.item_goal_id = static_cast<uint32_t>(strtoul(row[13], nullptr, 10));
e.item_id_list = row[14] ? row[14] : "";
e.item_list = row[15] ? row[15] : "";
e.dz_switch_id = static_cast<int32_t>(atoi(row[16]));
e.min_x = strtof(row[17], nullptr);
e.min_y = strtof(row[18], nullptr);
e.min_z = strtof(row[19], nullptr);
e.max_x = strtof(row[20], nullptr);
e.max_y = strtof(row[21], nullptr);
e.max_z = strtof(row[22], nullptr);
e.skill_list = row[23] ? row[23] : "";
e.spell_list = row[24] ? row[24] : "";
e.zones = row[25] ? row[25] : "";
e.zone_version = static_cast<int32_t>(atoi(row[26]));
e.optional = static_cast<int8_t>(atoi(row[27]));
e.npc_match_list = row[9] ? row[9] : "";
e.item_id_list = row[10] ? row[10] : "";
e.item_list = row[11] ? row[11] : "";
e.dz_switch_id = static_cast<int32_t>(atoi(row[12]));
e.min_x = strtof(row[13], nullptr);
e.min_y = strtof(row[14], nullptr);
e.min_z = strtof(row[15], nullptr);
e.max_x = strtof(row[16], nullptr);
e.max_y = strtof(row[17], nullptr);
e.max_z = strtof(row[18], nullptr);
e.skill_list = row[19] ? row[19] : "";
e.spell_list = row[20] ? row[20] : "";
e.zones = row[21] ? row[21] : "";
e.zone_version = static_cast<int32_t>(atoi(row[22]));
e.optional = static_cast<int8_t>(atoi(row[23]));
return e;
}
@ -292,25 +272,21 @@ public:
v.push_back(columns[6] + " = " + std::to_string(e.goalmethod));
v.push_back(columns[7] + " = " + std::to_string(e.goalcount));
v.push_back(columns[8] + " = '" + Strings::Escape(e.description_override) + "'");
v.push_back(columns[9] + " = " + std::to_string(e.npc_id));
v.push_back(columns[10] + " = " + std::to_string(e.npc_goal_id));
v.push_back(columns[11] + " = '" + Strings::Escape(e.npc_match_list) + "'");
v.push_back(columns[12] + " = " + std::to_string(e.item_id));
v.push_back(columns[13] + " = " + std::to_string(e.item_goal_id));
v.push_back(columns[14] + " = '" + Strings::Escape(e.item_id_list) + "'");
v.push_back(columns[15] + " = '" + Strings::Escape(e.item_list) + "'");
v.push_back(columns[16] + " = " + std::to_string(e.dz_switch_id));
v.push_back(columns[17] + " = " + std::to_string(e.min_x));
v.push_back(columns[18] + " = " + std::to_string(e.min_y));
v.push_back(columns[19] + " = " + std::to_string(e.min_z));
v.push_back(columns[20] + " = " + std::to_string(e.max_x));
v.push_back(columns[21] + " = " + std::to_string(e.max_y));
v.push_back(columns[22] + " = " + std::to_string(e.max_z));
v.push_back(columns[23] + " = '" + Strings::Escape(e.skill_list) + "'");
v.push_back(columns[24] + " = '" + Strings::Escape(e.spell_list) + "'");
v.push_back(columns[25] + " = '" + Strings::Escape(e.zones) + "'");
v.push_back(columns[26] + " = " + std::to_string(e.zone_version));
v.push_back(columns[27] + " = " + std::to_string(e.optional));
v.push_back(columns[9] + " = '" + Strings::Escape(e.npc_match_list) + "'");
v.push_back(columns[10] + " = '" + Strings::Escape(e.item_id_list) + "'");
v.push_back(columns[11] + " = '" + Strings::Escape(e.item_list) + "'");
v.push_back(columns[12] + " = " + std::to_string(e.dz_switch_id));
v.push_back(columns[13] + " = " + std::to_string(e.min_x));
v.push_back(columns[14] + " = " + std::to_string(e.min_y));
v.push_back(columns[15] + " = " + std::to_string(e.min_z));
v.push_back(columns[16] + " = " + std::to_string(e.max_x));
v.push_back(columns[17] + " = " + std::to_string(e.max_y));
v.push_back(columns[18] + " = " + std::to_string(e.max_z));
v.push_back(columns[19] + " = '" + Strings::Escape(e.skill_list) + "'");
v.push_back(columns[20] + " = '" + Strings::Escape(e.spell_list) + "'");
v.push_back(columns[21] + " = '" + Strings::Escape(e.zones) + "'");
v.push_back(columns[22] + " = " + std::to_string(e.zone_version));
v.push_back(columns[23] + " = " + std::to_string(e.optional));
auto results = db.QueryDatabase(
fmt::format(
@ -341,11 +317,7 @@ public:
v.push_back(std::to_string(e.goalmethod));
v.push_back(std::to_string(e.goalcount));
v.push_back("'" + Strings::Escape(e.description_override) + "'");
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.npc_goal_id));
v.push_back("'" + Strings::Escape(e.npc_match_list) + "'");
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_goal_id));
v.push_back("'" + Strings::Escape(e.item_id_list) + "'");
v.push_back("'" + Strings::Escape(e.item_list) + "'");
v.push_back(std::to_string(e.dz_switch_id));
@ -398,11 +370,7 @@ public:
v.push_back(std::to_string(e.goalmethod));
v.push_back(std::to_string(e.goalcount));
v.push_back("'" + Strings::Escape(e.description_override) + "'");
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.npc_goal_id));
v.push_back("'" + Strings::Escape(e.npc_match_list) + "'");
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_goal_id));
v.push_back("'" + Strings::Escape(e.item_id_list) + "'");
v.push_back("'" + Strings::Escape(e.item_list) + "'");
v.push_back(std::to_string(e.dz_switch_id));
@ -459,25 +427,21 @@ public:
e.goalmethod = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.goalcount = static_cast<int32_t>(atoi(row[7]));
e.description_override = row[8] ? row[8] : "";
e.npc_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.npc_goal_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.npc_match_list = row[11] ? row[11] : "";
e.item_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
e.item_goal_id = static_cast<uint32_t>(strtoul(row[13], nullptr, 10));
e.item_id_list = row[14] ? row[14] : "";
e.item_list = row[15] ? row[15] : "";
e.dz_switch_id = static_cast<int32_t>(atoi(row[16]));
e.min_x = strtof(row[17], nullptr);
e.min_y = strtof(row[18], nullptr);
e.min_z = strtof(row[19], nullptr);
e.max_x = strtof(row[20], nullptr);
e.max_y = strtof(row[21], nullptr);
e.max_z = strtof(row[22], nullptr);
e.skill_list = row[23] ? row[23] : "";
e.spell_list = row[24] ? row[24] : "";
e.zones = row[25] ? row[25] : "";
e.zone_version = static_cast<int32_t>(atoi(row[26]));
e.optional = static_cast<int8_t>(atoi(row[27]));
e.npc_match_list = row[9] ? row[9] : "";
e.item_id_list = row[10] ? row[10] : "";
e.item_list = row[11] ? row[11] : "";
e.dz_switch_id = static_cast<int32_t>(atoi(row[12]));
e.min_x = strtof(row[13], nullptr);
e.min_y = strtof(row[14], nullptr);
e.min_z = strtof(row[15], nullptr);
e.max_x = strtof(row[16], nullptr);
e.max_y = strtof(row[17], nullptr);
e.max_z = strtof(row[18], nullptr);
e.skill_list = row[19] ? row[19] : "";
e.spell_list = row[20] ? row[20] : "";
e.zones = row[21] ? row[21] : "";
e.zone_version = static_cast<int32_t>(atoi(row[22]));
e.optional = static_cast<int8_t>(atoi(row[23]));
all_entries.push_back(e);
}
@ -511,25 +475,21 @@ public:
e.goalmethod = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.goalcount = static_cast<int32_t>(atoi(row[7]));
e.description_override = row[8] ? row[8] : "";
e.npc_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.npc_goal_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.npc_match_list = row[11] ? row[11] : "";
e.item_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
e.item_goal_id = static_cast<uint32_t>(strtoul(row[13], nullptr, 10));
e.item_id_list = row[14] ? row[14] : "";
e.item_list = row[15] ? row[15] : "";
e.dz_switch_id = static_cast<int32_t>(atoi(row[16]));
e.min_x = strtof(row[17], nullptr);
e.min_y = strtof(row[18], nullptr);
e.min_z = strtof(row[19], nullptr);
e.max_x = strtof(row[20], nullptr);
e.max_y = strtof(row[21], nullptr);
e.max_z = strtof(row[22], nullptr);
e.skill_list = row[23] ? row[23] : "";
e.spell_list = row[24] ? row[24] : "";
e.zones = row[25] ? row[25] : "";
e.zone_version = static_cast<int32_t>(atoi(row[26]));
e.optional = static_cast<int8_t>(atoi(row[27]));
e.npc_match_list = row[9] ? row[9] : "";
e.item_id_list = row[10] ? row[10] : "";
e.item_list = row[11] ? row[11] : "";
e.dz_switch_id = static_cast<int32_t>(atoi(row[12]));
e.min_x = strtof(row[13], nullptr);
e.min_y = strtof(row[14], nullptr);
e.min_z = strtof(row[15], nullptr);
e.max_x = strtof(row[16], nullptr);
e.max_y = strtof(row[17], nullptr);
e.max_z = strtof(row[18], nullptr);
e.skill_list = row[19] ? row[19] : "";
e.spell_list = row[20] ? row[20] : "";
e.zones = row[21] ? row[21] : "";
e.zone_version = static_cast<int32_t>(atoi(row[22]));
e.optional = static_cast<int8_t>(atoi(row[23]));
all_entries.push_back(e);
}

View File

@ -25,15 +25,15 @@ public:
int8_t duration_code;
std::string title;
std::string description;
std::string reward;
uint32_t rewardid;
uint32_t cashreward;
int32_t xpreward;
uint8_t rewardmethod;
std::string reward_text;
std::string reward_id_list;
uint32_t cash_reward;
int32_t exp_reward;
uint8_t reward_method;
int32_t reward_points;
int32_t reward_point_type;
uint8_t minlevel;
uint8_t maxlevel;
uint8_t min_level;
uint8_t max_level;
uint32_t level_spread;
uint32_t min_players;
uint32_t max_players;
@ -63,15 +63,15 @@ public:
"duration_code",
"title",
"description",
"reward",
"rewardid",
"cashreward",
"xpreward",
"rewardmethod",
"reward_text",
"reward_id_list",
"cash_reward",
"exp_reward",
"reward_method",
"reward_points",
"reward_point_type",
"minlevel",
"maxlevel",
"min_level",
"max_level",
"level_spread",
"min_players",
"max_players",
@ -97,15 +97,15 @@ public:
"duration_code",
"title",
"description",
"reward",
"rewardid",
"cashreward",
"xpreward",
"rewardmethod",
"reward_text",
"reward_id_list",
"cash_reward",
"exp_reward",
"reward_method",
"reward_points",
"reward_point_type",
"minlevel",
"maxlevel",
"min_level",
"max_level",
"level_spread",
"min_players",
"max_players",
@ -165,15 +165,15 @@ public:
e.duration_code = 0;
e.title = "";
e.description = "";
e.reward = "";
e.rewardid = 0;
e.cashreward = 0;
e.xpreward = 0;
e.rewardmethod = 2;
e.reward_text = "";
e.reward_id_list = "";
e.cash_reward = 0;
e.exp_reward = 0;
e.reward_method = 0;
e.reward_points = 0;
e.reward_point_type = 0;
e.minlevel = 0;
e.maxlevel = 0;
e.min_level = 0;
e.max_level = 0;
e.level_spread = 0;
e.min_players = 0;
e.max_players = 0;
@ -228,15 +228,15 @@ public:
e.duration_code = static_cast<int8_t>(atoi(row[3]));
e.title = row[4] ? row[4] : "";
e.description = row[5] ? row[5] : "";
e.reward = row[6] ? row[6] : "";
e.rewardid = static_cast<uint32_t>(strtoul(row[7], nullptr, 10));
e.cashreward = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.xpreward = static_cast<int32_t>(atoi(row[9]));
e.rewardmethod = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
e.reward_text = row[6] ? row[6] : "";
e.reward_id_list = row[7] ? row[7] : "";
e.cash_reward = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.exp_reward = static_cast<int32_t>(atoi(row[9]));
e.reward_method = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
e.reward_points = static_cast<int32_t>(atoi(row[11]));
e.reward_point_type = static_cast<int32_t>(atoi(row[12]));
e.minlevel = static_cast<uint8_t>(strtoul(row[13], nullptr, 10));
e.maxlevel = static_cast<uint8_t>(strtoul(row[14], nullptr, 10));
e.min_level = static_cast<uint8_t>(strtoul(row[13], nullptr, 10));
e.max_level = static_cast<uint8_t>(strtoul(row[14], nullptr, 10));
e.level_spread = static_cast<uint32_t>(strtoul(row[15], nullptr, 10));
e.min_players = static_cast<uint32_t>(strtoul(row[16], nullptr, 10));
e.max_players = static_cast<uint32_t>(strtoul(row[17], nullptr, 10));
@ -289,15 +289,15 @@ public:
v.push_back(columns[3] + " = " + std::to_string(e.duration_code));
v.push_back(columns[4] + " = '" + Strings::Escape(e.title) + "'");
v.push_back(columns[5] + " = '" + Strings::Escape(e.description) + "'");
v.push_back(columns[6] + " = '" + Strings::Escape(e.reward) + "'");
v.push_back(columns[7] + " = " + std::to_string(e.rewardid));
v.push_back(columns[8] + " = " + std::to_string(e.cashreward));
v.push_back(columns[9] + " = " + std::to_string(e.xpreward));
v.push_back(columns[10] + " = " + std::to_string(e.rewardmethod));
v.push_back(columns[6] + " = '" + Strings::Escape(e.reward_text) + "'");
v.push_back(columns[7] + " = '" + Strings::Escape(e.reward_id_list) + "'");
v.push_back(columns[8] + " = " + std::to_string(e.cash_reward));
v.push_back(columns[9] + " = " + std::to_string(e.exp_reward));
v.push_back(columns[10] + " = " + std::to_string(e.reward_method));
v.push_back(columns[11] + " = " + std::to_string(e.reward_points));
v.push_back(columns[12] + " = " + std::to_string(e.reward_point_type));
v.push_back(columns[13] + " = " + std::to_string(e.minlevel));
v.push_back(columns[14] + " = " + std::to_string(e.maxlevel));
v.push_back(columns[13] + " = " + std::to_string(e.min_level));
v.push_back(columns[14] + " = " + std::to_string(e.max_level));
v.push_back(columns[15] + " = " + std::to_string(e.level_spread));
v.push_back(columns[16] + " = " + std::to_string(e.min_players));
v.push_back(columns[17] + " = " + std::to_string(e.max_players));
@ -338,15 +338,15 @@ public:
v.push_back(std::to_string(e.duration_code));
v.push_back("'" + Strings::Escape(e.title) + "'");
v.push_back("'" + Strings::Escape(e.description) + "'");
v.push_back("'" + Strings::Escape(e.reward) + "'");
v.push_back(std::to_string(e.rewardid));
v.push_back(std::to_string(e.cashreward));
v.push_back(std::to_string(e.xpreward));
v.push_back(std::to_string(e.rewardmethod));
v.push_back("'" + Strings::Escape(e.reward_text) + "'");
v.push_back("'" + Strings::Escape(e.reward_id_list) + "'");
v.push_back(std::to_string(e.cash_reward));
v.push_back(std::to_string(e.exp_reward));
v.push_back(std::to_string(e.reward_method));
v.push_back(std::to_string(e.reward_points));
v.push_back(std::to_string(e.reward_point_type));
v.push_back(std::to_string(e.minlevel));
v.push_back(std::to_string(e.maxlevel));
v.push_back(std::to_string(e.min_level));
v.push_back(std::to_string(e.max_level));
v.push_back(std::to_string(e.level_spread));
v.push_back(std::to_string(e.min_players));
v.push_back(std::to_string(e.max_players));
@ -395,15 +395,15 @@ public:
v.push_back(std::to_string(e.duration_code));
v.push_back("'" + Strings::Escape(e.title) + "'");
v.push_back("'" + Strings::Escape(e.description) + "'");
v.push_back("'" + Strings::Escape(e.reward) + "'");
v.push_back(std::to_string(e.rewardid));
v.push_back(std::to_string(e.cashreward));
v.push_back(std::to_string(e.xpreward));
v.push_back(std::to_string(e.rewardmethod));
v.push_back("'" + Strings::Escape(e.reward_text) + "'");
v.push_back("'" + Strings::Escape(e.reward_id_list) + "'");
v.push_back(std::to_string(e.cash_reward));
v.push_back(std::to_string(e.exp_reward));
v.push_back(std::to_string(e.reward_method));
v.push_back(std::to_string(e.reward_points));
v.push_back(std::to_string(e.reward_point_type));
v.push_back(std::to_string(e.minlevel));
v.push_back(std::to_string(e.maxlevel));
v.push_back(std::to_string(e.min_level));
v.push_back(std::to_string(e.max_level));
v.push_back(std::to_string(e.level_spread));
v.push_back(std::to_string(e.min_players));
v.push_back(std::to_string(e.max_players));
@ -456,15 +456,15 @@ public:
e.duration_code = static_cast<int8_t>(atoi(row[3]));
e.title = row[4] ? row[4] : "";
e.description = row[5] ? row[5] : "";
e.reward = row[6] ? row[6] : "";
e.rewardid = static_cast<uint32_t>(strtoul(row[7], nullptr, 10));
e.cashreward = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.xpreward = static_cast<int32_t>(atoi(row[9]));
e.rewardmethod = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
e.reward_text = row[6] ? row[6] : "";
e.reward_id_list = row[7] ? row[7] : "";
e.cash_reward = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.exp_reward = static_cast<int32_t>(atoi(row[9]));
e.reward_method = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
e.reward_points = static_cast<int32_t>(atoi(row[11]));
e.reward_point_type = static_cast<int32_t>(atoi(row[12]));
e.minlevel = static_cast<uint8_t>(strtoul(row[13], nullptr, 10));
e.maxlevel = static_cast<uint8_t>(strtoul(row[14], nullptr, 10));
e.min_level = static_cast<uint8_t>(strtoul(row[13], nullptr, 10));
e.max_level = static_cast<uint8_t>(strtoul(row[14], nullptr, 10));
e.level_spread = static_cast<uint32_t>(strtoul(row[15], nullptr, 10));
e.min_players = static_cast<uint32_t>(strtoul(row[16], nullptr, 10));
e.max_players = static_cast<uint32_t>(strtoul(row[17], nullptr, 10));
@ -508,15 +508,15 @@ public:
e.duration_code = static_cast<int8_t>(atoi(row[3]));
e.title = row[4] ? row[4] : "";
e.description = row[5] ? row[5] : "";
e.reward = row[6] ? row[6] : "";
e.rewardid = static_cast<uint32_t>(strtoul(row[7], nullptr, 10));
e.cashreward = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.xpreward = static_cast<int32_t>(atoi(row[9]));
e.rewardmethod = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
e.reward_text = row[6] ? row[6] : "";
e.reward_id_list = row[7] ? row[7] : "";
e.cash_reward = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.exp_reward = static_cast<int32_t>(atoi(row[9]));
e.reward_method = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
e.reward_points = static_cast<int32_t>(atoi(row[11]));
e.reward_point_type = static_cast<int32_t>(atoi(row[12]));
e.minlevel = static_cast<uint8_t>(strtoul(row[13], nullptr, 10));
e.maxlevel = static_cast<uint8_t>(strtoul(row[14], nullptr, 10));
e.min_level = static_cast<uint8_t>(strtoul(row[13], nullptr, 10));
e.max_level = static_cast<uint8_t>(strtoul(row[14], nullptr, 10));
e.level_spread = static_cast<uint32_t>(strtoul(row[15], nullptr, 10));
e.min_players = static_cast<uint32_t>(strtoul(row[16], nullptr, 10));
e.max_players = static_cast<uint32_t>(strtoul(row[17], nullptr, 10));

View File

@ -1,50 +0,0 @@
#ifndef EQEMU_GOALLISTS_REPOSITORY_H
#define EQEMU_GOALLISTS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_goallists_repository.h"
class GoallistsRepository: public BaseGoallistsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* GoallistsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* GoallistsRepository::GetWhereNeverExpires()
* GoallistsRepository::GetWhereXAndY()
* GoallistsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
};
#endif //EQEMU_GOALLISTS_REPOSITORY_H

View File

@ -64,7 +64,7 @@ std::vector<std::string> Strings::Split(const std::string &str, const char delim
}
// this one takes delimiter length into consideration
std::vector<std::string> Strings::Split(std::string s, std::string delimiter)
std::vector<std::string> Strings::Split(const std::string& s, const std::string& delimiter)
{
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
std::string token;
@ -674,3 +674,7 @@ std::string Strings::ConvertToDigit(int n, std::string suffix)
return NUM_TO_ENGLISH_X[n] + suffix;
}
}
bool Strings::Contains(const std::string& subject, const std::string& search)
{
return subject.find(search) != std::string::npos;
}

View File

@ -84,6 +84,7 @@ namespace EQ {
class Strings {
public:
static bool Contains(std::vector<std::string> container, std::string element);
static bool Contains(const std::string& subject, const std::string& search);
static bool IsNumber(const std::string &s);
static const std::string ToLower(std::string s);
static const std::string ToUpper(std::string s);
@ -105,7 +106,7 @@ public:
static std::string SecondsToTime(int duration, bool is_milliseconds = false);
static std::string::size_type SearchDelim(const std::string &haystack, const std::string &needle, const char deliminator = ',');
static std::vector<std::string> Split(const std::string &s, const char delim = ',');
static std::vector<std::string> Split(std::string s, std::string delimiter);
static std::vector<std::string> Split(const std::string& s, const std::string& delimiter);
static std::vector<std::string> Wrap(std::vector<std::string> &src, std::string character);
static void FindReplace(std::string &string_subject, const std::string &search_string, const std::string &replace_string);

View File

@ -1,6 +1,7 @@
#ifndef EQEMU_TASKS_H
#define EQEMU_TASKS_H
#include "../common/strings.h"
#include "serialize_buffer.h"
#include <algorithm>
#include <array>
@ -16,12 +17,10 @@
// Command Codes for worldserver ServerOP_ReloadTasks
#define RELOADTASKS 0
#define RELOADTASKGOALLISTS 1
#define RELOADTASKSETS 2
typedef enum {
METHODSINGLEID = 0,
METHODLIST = 1,
METHODQUEST = 2
} TaskMethodType;
@ -77,11 +76,7 @@ struct ActivityInformation {
int spell_id; // older clients, first id from above
TaskMethodType goal_method;
int goal_count;
uint32_t npc_id;
uint32_t npc_goal_id;
std::string npc_match_list; // delimited by '|' for partial name matches but also supports ids
uint32_t item_id;
uint32_t item_goal_id;
std::string item_id_list; // delimited by '|' to support multiple item ids
int dz_switch_id;
float min_x;
@ -207,15 +202,15 @@ enum class DurationCode {
struct TaskInformation {
TaskType type;
int duration{};
uint32_t duration{};
DurationCode duration_code; // description for time investment for when duration == 0
std::string title{}; // max length 64
std::string description{}; // max length 4000, 2048 on Tit
std::string reward{};
std::string item_link{}; // max length 128 older clients, item link gets own string
std::string completion_emote{}; // emote after completing task, yellow. Maybe should make more generic ... but yellow for now!
int reward_id{};
int cash_reward{}; // Expressed in copper
std::string reward_id_list{};
uint32_t cash_reward{}; // Expressed in copper
int experience_reward{};
int faction_reward{}; // npc_faction_id if amount == 0, otherwise primary faction ID
int faction_amount{}; // faction hit value
@ -225,14 +220,14 @@ struct TaskInformation {
int activity_count{};
uint8_t min_level{};
uint8_t max_level{};
int level_spread;
int min_players;
int max_players;
uint32_t level_spread;
uint32_t min_players;
uint32_t max_players;
bool repeatable{};
int replay_timer_group;
int replay_timer_seconds;
int request_timer_group;
int request_timer_seconds;
uint32_t replay_timer_group;
uint32_t replay_timer_seconds;
uint32_t request_timer_group;
uint32_t request_timer_seconds;
ActivityInformation activity_information[MAXACTIVITIESPERTASK];
void SerializeSelector(SerializeBuffer& out, EQ::versions::ClientVersion client_version) const
@ -420,6 +415,31 @@ namespace Tasks {
return result;
}
static bool IsInMatchList(const std::string& match_list, const std::string& entry)
{
for (auto &s: Strings::Split(match_list, '|')) {
if (s == entry) {
return true;
}
}
return false;
}
static bool IsInMatchListPartial(const std::string &match_list, const std::string &entry)
{
std::string entry_match = Strings::ToLower(entry);
for (auto &s: Strings::Split(match_list, '|')) {
if (entry_match.find(Strings::ToLower(s)) != std::string::npos) {
return true;
}
}
return false;
}
}
namespace TaskStr {

View File

@ -34,7 +34,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9207
#define CURRENT_BINARY_DATABASE_VERSION 9208
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029

View File

@ -461,6 +461,7 @@
9205|2022_09_03_fix_starting_point_heading.sql|SELECT * FROM db_version WHERE version >= 9205|empty|
9206|2022_09_03_fix_door_destination_headings.sql|SELECT * FROM db_version WHERE version >= 9206|empty|
9207|2022_09_03_fix_zone_point_heading_data.sql|SELECT * FROM db_version WHERE version >= 9207|empty|
9208|2022_09_25_task_concat_matchlists.sql|SHOW COLUMNS FROM `task_activities` LIKE 'npc_id'|not_empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,69 @@
SET SESSION group_concat_max_len = 1048576;
-- backup original(s)
CREATE TABLE `goallists_backup_9_25_2022` LIKE `goallists`;
INSERT INTO `goallists_backup_9_25_2022` SELECT * FROM `goallists`;
CREATE TABLE `tasks_backup_9_25_2022` LIKE `tasks`;
INSERT INTO `tasks_backup_9_25_2022` SELECT * FROM `tasks`;
-- npc id
UPDATE `task_activities`
SET `task_activities`.`npc_match_list` = CONCAT_WS('|', `npc_match_list`, `npc_id`)
WHERE npc_id != 0;
-- npc_goal_id goallists
UPDATE `task_activities`
INNER JOIN
(
SELECT `goallists`.`listid`, GROUP_CONCAT(`goallists`.`entry` ORDER BY `goallists`.`entry` SEPARATOR '|') AS `goallist_ids`
FROM `goallists`
GROUP BY `goallists`.`listid`
) AS `goallist_group`
ON `task_activities`.`npc_goal_id` = `goallist_group`.`listid`
SET `task_activities`.`npc_match_list` = CONCAT_WS('|', `npc_match_list`, `goallist_ids`)
WHERE npc_goal_id != 0;
-- item id
UPDATE `task_activities`
SET `task_activities`.`item_id_list` = CONCAT_WS('|', `item_id_list`, `item_id`)
WHERE item_id != 0;
-- item_goal_id goallists
UPDATE `task_activities`
INNER JOIN
(
SELECT `goallists`.`listid`, GROUP_CONCAT(`goallists`.`entry` ORDER BY `goallists`.`entry` SEPARATOR '|') AS `goallist_ids`
FROM `goallists`
GROUP BY `goallists`.`listid`
) AS `goallist_group`
ON `task_activities`.`item_goal_id` = `goallist_group`.`listid`
SET `task_activities`.`item_id_list` = CONCAT_WS('|', `item_id_list`, `goallist_ids`)
WHERE item_goal_id != 0;
ALTER TABLE `task_activities`
DROP COLUMN `npc_id`,
DROP COLUMN `npc_goal_id`,
DROP COLUMN `item_id`,
DROP COLUMN `item_goal_id`;
-- Reward cleanup and task table cleanup
ALTER TABLE `tasks`
CHANGE COLUMN `reward` `reward_text` varchar(64) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `description`,
CHANGE COLUMN `rewardid` `reward_id_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL AFTER `reward_text`,
CHANGE COLUMN `cashreward` `cash_reward` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `reward_id_list`,
CHANGE COLUMN `rewardmethod` `reward_method` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `xpreward`,
CHANGE COLUMN `minlevel` `min_level` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `reward_point_type`,
CHANGE COLUMN `maxlevel` `max_level` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `min_level`;
ALTER Table `tasks` CHANGE COLUMN `xpreward` `exp_reward` int(10) NOT NULL DEFAULT 0 AFTER `cash_reward`;
UPDATE tasks SET reward_id_list =
(
SELECT GROUP_CONCAT(`goallists`.`entry` ORDER BY `goallists`.`entry` SEPARATOR '|') AS `goallist_ids` FROM `goallists` WHERE listid = reward_id_list)
WHERE
reward_method = 1;
-- deprecated table
DROP table goallists;

View File

@ -1287,13 +1287,13 @@ bool SharedTaskManager::CanRequestSharedTask(uint32_t task_id, const SharedTaskR
}
// check if any party member's minimum level is too low (pre-2014 this was average level)
if (task.minlevel > 0 && request.lowest_level < task.minlevel) {
if (task.min_level > 0 && request.lowest_level < task.min_level) {
client_list.SendCharacterMessage(request.leader_id, Chat::Red, TaskStr::Get(TaskStr::LVL_TOO_LOW));
return false;
}
// check if any party member's maximum level is too high (pre-2014 this was average level)
if (task.maxlevel > 0 && request.highest_level > task.maxlevel) {
if (task.max_level > 0 && request.highest_level > task.max_level) {
client_list.SendCharacterMessage(request.leader_id, Chat::Red, TaskStr::Get(TaskStr::LVL_TOO_HIGH));
return false;
}
@ -1472,13 +1472,13 @@ bool SharedTaskManager::CanAddPlayer(SharedTask *s, uint32_t character_id, std::
}
// check if player is below minimum level of task (pre-2014 this was average level)
if (s->GetTaskData().minlevel > 0 && cle->level() < s->GetTaskData().minlevel) {
if (s->GetTaskData().min_level > 0 && cle->level() < s->GetTaskData().min_level) {
SendLeaderMessage(s, Chat::Red, TaskStr::Get(TaskStr::CANT_ADD_MIN_LEVEL));
allow_invite = false;
}
// check if player is above maximum level of task (pre-2014 this was average level)
if (s->GetTaskData().maxlevel > 0 && cle->level() > s->GetTaskData().maxlevel) {
if (s->GetTaskData().max_level > 0 && cle->level() > s->GetTaskData().max_level) {
SendLeaderMessage(s, Chat::Red, TaskStr::Get(TaskStr::CANT_ADD_MAX_LEVEL));
allow_invite = false;
}

View File

@ -140,7 +140,6 @@ SET(zone_sources
spell_effects.cpp
spells.cpp
task_client_state.cpp
task_goal_list_manager.cpp
task_manager.cpp
tasks.cpp
titles.cpp
@ -265,7 +264,6 @@ SET(zone_headers
spawngroup.h
string_ids.h
task_client_state.h
task_goal_list_manager.h
task_manager.h
tasks.h
titles.h

View File

@ -13,30 +13,31 @@ void command_findtask(Client *c, const Seperator *sep)
if (sep->IsNumber(1)) {
auto task_id = std::stoul(sep->arg[1]);
auto task_name = task_manager->GetTaskName(task_id);
auto task_message = (
std::string task_message = (
!task_name.empty() ?
fmt::format(
"Task {}: {}",
task_id,
task_name
).c_str() :
) :
fmt::format(
"Task ID {} was not found.",
task_id
).c_str()
)
);
c->Message(
Chat::White,
task_message
task_message.c_str()
);
}
else {
std::string search_criteria = Strings::ToLower(sep->argplus[1]);
if (!search_criteria.empty()) {
int found_count = 0;
for (const auto& task : task_manager->GetTaskData()) {
auto task_name = task.second.title;
int found_count = 0;
for (const auto &task: task_manager->GetTaskData()) {
auto task_name = task.second.title;
std::string task_name_lower = Strings::ToLower(task_name);
if (task_name_lower.find(search_criteria) == std::string::npos) {
continue;

View File

@ -191,12 +191,7 @@ void command_task(Client *c, const Seperator *sep)
return;
} else if (is_reload) {
if (arguments >= 2) {
if (!strcasecmp(sep->arg[2], "lists")) {
c->Message(Chat::Yellow, "Attempting to reload goal lists.");
worldserver.SendReloadTasks(RELOADTASKGOALLISTS);
c->Message(Chat::Yellow, "Successfully reloaded goal lists.");
return;
} else if (!strcasecmp(sep->arg[2], "sets")) {
if (!strcasecmp(sep->arg[2], "sets")) {
c->Message(Chat::Yellow, "Attempting to reload task sets.");
worldserver.SendReloadTasks(RELOADTASKSETS);
c->Message(Chat::Yellow, "Successfully reloaded task sets.");

View File

@ -492,19 +492,6 @@ bool ClientTaskState::CanUpdate(Client* client, const TaskUpdateFilter& filter,
return false;
}
// item is only checked for updates that provide an item (unlike npc which may be null for non-npcs)
if (activity.item_id != 0 && filter.item_id != 0 && activity.item_id != filter.item_id)
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed item id filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
if (activity.npc_id != 0 && (!filter.npc || activity.npc_id != filter.npc->GetNPCTypeID()))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed npc id filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
if (!activity.CheckZone(zone->GetZoneID(), zone->GetInstanceVersion()))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed zone filter", client->GetName(), task_id, client_activity.activity_id);
@ -523,32 +510,19 @@ bool ClientTaskState::CanUpdate(Client* client, const TaskUpdateFilter& filter,
}
}
if (activity.item_goal_id != 0 && filter.item_id != 0 &&
!task_manager->m_goal_list_manager.IsInList(activity.item_goal_id, filter.item_id))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed item goallist filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
// item is only checked for updates that provide an item to check (unlike npc which may be null for non-npcs)
if (!activity.item_id_list.empty() && filter.item_id != 0 &&
!TaskGoalListManager::IsInMatchList(activity.item_id_list, std::to_string(filter.item_id)))
!Tasks::IsInMatchList(activity.item_id_list, std::to_string(filter.item_id)))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed item match filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
if (activity.npc_goal_id != 0 && (!filter.npc ||
!task_manager->m_goal_list_manager.IsInList(activity.npc_goal_id, filter.npc->GetNPCTypeID())))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed npc goallist filter", client->GetName(), task_id, client_activity.activity_id);
return false;
}
// npc filter supports both npc names and ids in match lists
if (!activity.npc_match_list.empty() && (!filter.npc ||
(!TaskGoalListManager::IsInMatchListPartial(activity.npc_match_list, filter.npc->GetName()) &&
!TaskGoalListManager::IsInMatchListPartial(activity.npc_match_list, filter.npc->GetCleanName()) &&
!TaskGoalListManager::IsInMatchList(activity.npc_match_list, std::to_string(filter.npc->GetNPCTypeID())))))
(!Tasks::IsInMatchListPartial(activity.npc_match_list, filter.npc->GetName()) &&
!Tasks::IsInMatchListPartial(activity.npc_match_list, filter.npc->GetCleanName()) &&
!Tasks::IsInMatchList(activity.npc_match_list, std::to_string(filter.npc->GetNPCTypeID())))))
{
LogTasks("[CanUpdate] client [{}] task [{}]-[{}] failed npc match filter", client->GetName(), task_id, client_activity.activity_id);
return false;
@ -985,76 +959,68 @@ void ClientTaskState::DispatchEventTaskComplete(Client* client, ClientTaskInform
parse->EventPlayer(EVENT_TASK_COMPLETE, client, export_string, 0);
}
void ClientTaskState::RewardTask(Client *client, const TaskInformation *task_information, ClientTaskInformation& client_task)
void ClientTaskState::RewardTask(Client *c, const TaskInformation *ti, ClientTaskInformation& client_task)
{
if (!task_information || !client || client_task.was_rewarded) {
if (!ti || !c || client_task.was_rewarded) {
return;
}
client_task.was_rewarded = true;
client_task.updated = true;
if (!task_information->completion_emote.empty()) {
client->Message(Chat::Yellow, task_information->completion_emote.c_str());
if (!ti->completion_emote.empty()) {
c->Message(Chat::Yellow, ti->completion_emote.c_str());
}
// TODO: this function should sometimes use QuestReward_Struct and CashReward_Struct
// assumption is they use QuestReward_Struct when there is more than 1 thing getting rewarded
const EQ::ItemData *item_data;
std::vector<int> reward_list;
switch (task_information->reward_method) {
case METHODSINGLEID: {
if (task_information->reward_id) {
int16_t slot = client->GetInv().FindFreeSlot(false, true);
client->SummonItem(task_information->reward_id, -1, 0, 0, 0, 0, 0, 0, false, slot);
item_data = database.GetItem(task_information->reward_id);
if (item_data) {
client->MessageString(Chat::Yellow, YOU_HAVE_BEEN_GIVEN, item_data->Name);
if (ti->reward_method != METHODQUEST) {
for (const auto &i: Strings::Split(ti->reward_id_list, "|")) {
// handle charges
int16 charges = -1;
uint32 item_id = Strings::IsNumber(i) ? std::stoi(i) : 0;
if (Strings::Contains(i, ",")) {
auto s = Strings::Split(i, ",");
if (!s.empty() && s.size() == 2) {
item_id = Strings::IsNumber(s[0]) ? std::stoi(s[0]) : 0;
charges = Strings::IsNumber(s[1]) ? std::stoi(s[1]) : 0;
}
}
break;
}
case METHODLIST: {
reward_list = task_manager->m_goal_list_manager.GetListContents(task_information->reward_id);
for (int item_id : reward_list) {
int16_t slot = client->GetInv().FindFreeSlot(false, true);
client->SummonItem(item_id, -1, 0, 0, 0, 0, 0, 0, false, slot);
item_data = database.GetItem(item_id);
if (item_data) {
client->MessageString(Chat::Yellow, YOU_HAVE_BEEN_GIVEN, item_data->Name);
if (item_id > 0) {
std::unique_ptr<EQ::ItemInstance> inst(database.CreateItem(item_id, charges));
if (inst && inst->GetItem()) {
bool stacked = c->TryStacking(inst.get());
if (!stacked) {
int16_t slot = c->GetInv().FindFreeSlot(inst->IsClassBag(), true, inst->GetItem()->Size);
c->SummonItem(item_id, charges, 0, 0, 0, 0, 0, 0, false, slot);
}
c->MessageString(Chat::Yellow, YOU_HAVE_BEEN_GIVEN, inst->GetItem()->Name);
}
}
break;
}
default: {
// Nothing special done for METHODQUEST
break;
}
}
// just use normal NPC faction ID stuff
if (task_information->faction_reward && task_information->faction_amount == 0) {
client->SetFactionLevel(
client->CharacterID(),
task_information->faction_reward,
client->GetBaseClass(),
client->GetBaseRace(),
client->GetDeity()
if (ti->faction_reward && ti->faction_amount == 0) {
c->SetFactionLevel(
c->CharacterID(),
ti->faction_reward,
c->GetBaseClass(),
c->GetBaseRace(),
c->GetDeity()
);
} else if (task_information->faction_reward != 0 && task_information->faction_amount != 0) {
client->RewardFaction(
task_information->faction_reward,
task_information->faction_amount
} else if (ti->faction_reward != 0 && ti->faction_amount != 0) {
c->RewardFaction(
ti->faction_reward,
ti->faction_amount
);
}
if (task_information->cash_reward) {
if (ti->cash_reward) {
int platinum, gold, silver, copper;
copper = task_information->cash_reward;
copper = ti->cash_reward;
platinum = copper / 1000;
copper = copper - (platinum * 1000);
@ -1063,11 +1029,11 @@ void ClientTaskState::RewardTask(Client *client, const TaskInformation *task_inf
silver = copper / 10;
copper = copper - (silver * 10);
client->CashReward(copper, silver, gold, platinum);
c->CashReward(copper, silver, gold, platinum);
}
int32 experience_reward = task_information->experience_reward;
int32 experience_reward = ti->experience_reward;
if (experience_reward > 0) {
client->AddEXP(experience_reward);
c->AddEXP(experience_reward);
}
if (experience_reward < 0) {
uint32 pos_reward = experience_reward * -1;
@ -1075,19 +1041,19 @@ void ClientTaskState::RewardTask(Client *client, const TaskInformation *task_inf
if (pos_reward > 100 && pos_reward < 25700) {
uint8 max_level = pos_reward / 100;
uint8 exp_percent = pos_reward - (max_level * 100);
client->AddLevelBasedExp(exp_percent, max_level);
c->AddLevelBasedExp(exp_percent, max_level);
}
}
if (task_information->reward_points > 0)
if (ti->reward_points > 0)
{
if (task_information->reward_point_type == AltCurrencyType::RadiantCrystal)
if (ti->reward_point_type == AltCurrencyType::RadiantCrystal)
{
client->AddCrystals(task_information->reward_points, 0);
c->AddCrystals(ti->reward_points, 0);
}
else if (task_information->reward_point_type == AltCurrencyType::EbonCrystal)
else if (ti->reward_point_type == AltCurrencyType::EbonCrystal)
{
client->AddCrystals(0, task_information->reward_points);
c->AddCrystals(0, ti->reward_points);
}
}
}

View File

@ -65,7 +65,7 @@ public:
bool TaskOutOfTime(TaskType task_type, int index);
void TaskPeriodicChecks(Client *client);
void SendTaskHistory(Client *client, int task_index);
void RewardTask(Client* client, const TaskInformation* task_information, ClientTaskInformation& client_task);
void RewardTask(Client* c, const TaskInformation* ti, ClientTaskInformation& client_task);
void EnableTask(int character_id, int task_count, int *task_list);
void DisableTask(int character_id, int task_count, int *task_list);
bool IsTaskEnabled(int task_id);

View File

@ -1,172 +0,0 @@
#include "../common/global_define.h"
#include "../common/misc_functions.h"
#include "../common/repositories/goallists_repository.h"
#include "../common/rulesys.h"
#include "client.h"
#include "mob.h"
#include "quest_parser_collection.h"
#include "task_goal_list_manager.h"
#include "tasks.h"
#include "zonedb.h"
TaskGoalListManager::TaskGoalListManager()
{
m_goal_lists_count = 0;
}
TaskGoalListManager::~TaskGoalListManager() {}
bool TaskGoalListManager::LoadLists()
{
m_task_goal_lists.clear();
m_goal_lists_count = 0;
std::string query = "SELECT `listid`, COUNT(`entry`) FROM `goallists` GROUP by `listid` ORDER BY `listid`";
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
return false;
}
m_goal_lists_count = results.RowCount();
LogTasks("Loaded [{}] GoalLists", m_goal_lists_count);
m_task_goal_lists.reserve(m_goal_lists_count);
int list_index = 0;
for (auto row = results.begin(); row != results.end(); ++row) {
int list_id = atoi(row[0]);
int list_size = atoi(row[1]);
m_task_goal_lists.push_back({list_id, 0, 0});
m_task_goal_lists[list_index].GoalItemEntries.reserve(list_size);
list_index++;
}
auto goal_lists = GoallistsRepository::GetWhere(content_db, "TRUE ORDER BY listid, entry ASC");
for (list_index = 0; list_index < m_goal_lists_count; list_index++) {
int list_id = m_task_goal_lists[list_index].ListID;
for (auto &entry: goal_lists) {
if (entry.listid == list_id) {
if (entry.entry < m_task_goal_lists[list_index].Min) {
m_task_goal_lists[list_index].Min = entry.entry;
}
if (entry.entry > m_task_goal_lists[list_index].Max) {
m_task_goal_lists[list_index].Max = entry.entry;
}
m_task_goal_lists[list_index].GoalItemEntries.push_back(entry.entry);
LogTasksDetail(
"Goal list index [{}] loading list [{}] entry [{}]",
list_index,
list_id,
entry.entry
);
}
}
}
return true;
}
int TaskGoalListManager::GetListByID(int list_id)
{
// Find the list with the specified ListID and return the index
auto it = std::find_if(
m_task_goal_lists.begin(),
m_task_goal_lists.end(),
[list_id](const TaskGoalList_Struct &t) { return t.ListID == list_id; }
);
if (it == m_task_goal_lists.end()) {
return -1;
}
return std::distance(m_task_goal_lists.begin(), it);
}
int TaskGoalListManager::GetFirstEntry(int list_id)
{
int list_by_id = GetListByID(list_id);
if ((list_by_id < 0) || (list_by_id >= m_goal_lists_count)) {
return -1;
}
if (m_task_goal_lists[list_by_id].GoalItemEntries.empty()) {
return -1;
}
return m_task_goal_lists[list_by_id].GoalItemEntries[0];
}
std::vector<int> TaskGoalListManager::GetListContents(int list_index)
{
std::vector<int> list_contents;
int list_by_id = GetListByID(list_index);
if ((list_by_id < 0) || (list_by_id >= m_goal_lists_count)) {
return list_contents;
}
list_contents = m_task_goal_lists[list_by_id].GoalItemEntries;
return list_contents;
}
bool TaskGoalListManager::IsInList(int list_id, int entry)
{
Log(Logs::General, Logs::Tasks, "[UPDATE] TaskGoalListManager::IsInList(%i, %i)", list_id, entry);
int list_index = GetListByID(list_id);
if ((list_index < 0) || (list_index >= m_goal_lists_count)) {
return false;
}
if ((entry < m_task_goal_lists[list_index].Min) || (entry > m_task_goal_lists[list_index].Max)) {
return false;
}
int first_entry = 0;
auto &task = m_task_goal_lists[list_index];
auto it = std::find(task.GoalItemEntries.begin(), task.GoalItemEntries.end(), entry);
if (it == task.GoalItemEntries.end()) {
return false;
}
Log(Logs::General, Logs::Tasks, "[UPDATE] TaskGoalListManager::IsInList(%i, %i) returning true", list_index, entry);
return true;
}
bool TaskGoalListManager::IsInMatchList(const std::string& match_list, const std::string& entry)
{
for (auto &s: Strings::Split(match_list, '|')) {
if (s == entry) {
return true;
}
}
return false;
}
bool TaskGoalListManager::IsInMatchListPartial(const std::string &match_list, const std::string &entry)
{
std::string entry_match = Strings::ToLower(entry);
for (auto &s: Strings::Split(match_list, '|')) {
if (entry_match.find(Strings::ToLower(s)) != std::string::npos) {
return true;
}
}
return false;
}

View File

@ -1,39 +0,0 @@
#ifndef EQEMU_TASK_GOAL_LIST_MANAGER_H
#define EQEMU_TASK_GOAL_LIST_MANAGER_H
#include "tasks.h"
#include "../common/types.h"
#include <list>
#include <vector>
#include <string>
#include <algorithm>
struct TaskGoalList_Struct {
int ListID;
int Min, Max;
std::vector<int> GoalItemEntries;
};
// This is used for handling lists, loading them from the database, searching them.
// Used for lists of NPCs to kill, items to loot, etc, as well as lists of items to
// reward the player with on completion of the task.
class TaskGoalListManager {
public:
TaskGoalListManager();
~TaskGoalListManager();
bool LoadLists();
int GetListByID(int list_id);
bool IsInList(int list_id, int entry);
int GetFirstEntry(int list_id);
std::vector<int> GetListContents(int list_index);
static bool IsInMatchList(const std::string& match_list, const std::string& entry);
static bool IsInMatchListPartial(const std::string& match_list, const std::string& entry);
private:
std::vector<TaskGoalList_Struct> m_task_goal_lists;
int m_goal_lists_count;
};
#endif //EQEMU_TASK_GOAL_LIST_MANAGER_H

View File

@ -41,22 +41,11 @@ bool TaskManager::LoadTaskSets()
return true;
}
void TaskManager::ReloadGoalLists()
{
if (!m_goal_list_manager.LoadLists()) {
Log(Logs::Detail, Logs::Tasks, "TaskManager::LoadTasks LoadLists failed");
}
}
bool TaskManager::LoadTasks(int single_task)
{
std::string task_query_filter = fmt::format("id = {}", single_task);
std::string query;
if (single_task == 0) {
if (!m_goal_list_manager.LoadLists()) {
LogTasks("[TaskManager::LoadTasks] LoadLists failed");
}
if (!LoadTaskSets()) {
LogTasks("[TaskManager::LoadTasks] LoadTaskSets failed");
}
@ -78,40 +67,40 @@ bool TaskManager::LoadTasks(int single_task)
}
// load task data
TaskInformation task_data{};
task_data.type = static_cast<TaskType>(task.type);
task_data.duration = task.duration;
task_data.duration_code = static_cast<DurationCode>(task.duration_code);
task_data.title = task.title;
task_data.description = task.description;
task_data.reward = task.reward;
task_data.reward_id = task.rewardid;
task_data.cash_reward = task.cashreward;
task_data.experience_reward = task.xpreward;
task_data.reward_method = (TaskMethodType) task.rewardmethod;
task_data.reward_points = task.reward_points;
task_data.reward_point_type = static_cast<AltCurrencyType>(task.reward_point_type);
task_data.faction_reward = task.faction_reward;
task_data.faction_amount = task.faction_amount;
task_data.min_level = task.minlevel;
task_data.max_level = task.maxlevel;
task_data.level_spread = task.level_spread;
task_data.min_players = task.min_players;
task_data.max_players = task.max_players;
task_data.repeatable = task.repeatable;
task_data.completion_emote = task.completion_emote;
task_data.replay_timer_group = task.replay_timer_group;
task_data.replay_timer_seconds = task.replay_timer_seconds;
task_data.request_timer_group = task.request_timer_group;
task_data.request_timer_seconds = task.request_timer_seconds;
task_data.activity_count = 0;
TaskInformation ti{};
ti.type = static_cast<TaskType>(task.type);
ti.duration = task.duration;
ti.duration_code = static_cast<DurationCode>(task.duration_code);
ti.title = task.title;
ti.description = task.description;
ti.reward = task.reward_text;
ti.reward_id_list = task.reward_id_list;
ti.cash_reward = task.cash_reward;
ti.experience_reward = task.exp_reward;
ti.reward_method = (TaskMethodType) task.reward_method;
ti.reward_points = task.reward_points;
ti.reward_point_type = static_cast<AltCurrencyType>(task.reward_point_type);
ti.faction_reward = task.faction_reward;
ti.faction_amount = task.faction_amount;
ti.min_level = task.min_level;
ti.max_level = task.max_level;
ti.level_spread = task.level_spread;
ti.min_players = task.min_players;
ti.max_players = task.max_players;
ti.repeatable = task.repeatable;
ti.completion_emote = task.completion_emote;
ti.replay_timer_group = task.replay_timer_group;
ti.replay_timer_seconds = task.replay_timer_seconds;
ti.request_timer_group = task.request_timer_group;
ti.request_timer_seconds = task.request_timer_seconds;
ti.activity_count = 0;
m_task_data.try_emplace(task_id, std::move(task_data));
m_task_data.try_emplace(task_id, std::move(ti));
LogTasksDetail(
"[LoadTasks] (Task) task_id [{}] type [{}] () duration [{}] duration_code [{}] title [{}] description [{}] "
" reward [{}] rewardid [{}] cashreward [{}] xpreward [{}] rewardmethod [{}] faction_reward [{}] minlevel [{}] "
" maxlevel [{}] level_spread [{}] min_players [{}] max_players [{}] repeatable [{}] completion_emote [{}]",
" reward_text [{}] reward_id_list [{}] cash_reward [{}] exp_reward [{}] reward_method [{}] faction_reward [{}] min_level [{}] "
" max_level [{}] level_spread [{}] min_players [{}] max_players [{}] repeatable [{}] completion_emote [{}]",
" replay_group [{}] replay_timer_seconds [{}] request_group [{}] request_timer_seconds [{}]",
task.id,
task.type,
@ -119,14 +108,14 @@ bool TaskManager::LoadTasks(int single_task)
task.duration_code,
task.title,
task.description,
task.reward,
task.rewardid,
task.cashreward,
task.xpreward,
task.rewardmethod,
task.reward_text,
task.reward_id_list,
task.cash_reward,
task.exp_reward,
task.reward_method,
task.faction_reward,
task.minlevel,
task.maxlevel,
task.min_level,
task.max_level,
task.level_spread,
task.min_players,
task.max_players,
@ -157,10 +146,10 @@ bool TaskManager::LoadTasks(int single_task)
// load activity data
auto task_activities = TaskActivitiesRepository::GetWhere(content_db, activities_query_filter);
for (auto &task_activity: task_activities) {
int task_id = task_activity.taskid;
int step = task_activity.step;
int activity_id = task_activity.activityid;
for (auto &a: task_activities) {
int task_id = a.taskid;
int step = a.step;
int activity_id = a.activityid;
if (task_id <= 0 || activity_id < 0 || activity_id >= MAXACTIVITIESPERTASK) {
@ -185,7 +174,7 @@ bool TaskManager::LoadTasks(int single_task)
// create pointer to activity data since declarations get unruly long
int activity_index = task_data->activity_count;
ActivityInformation *activity_data = &task_data->activity_information[activity_index];
ActivityInformation *ad = &task_data->activity_information[activity_index];
// Task Activities MUST be numbered sequentially from 0. If not, log an error
// and set the task to nullptr. Subsequent activities for this task will raise
@ -202,53 +191,49 @@ bool TaskManager::LoadTasks(int single_task)
}
// set activity data
activity_data->req_activity_id = task_activity.req_activity_id;
activity_data->step = step;
activity_data->activity_type = static_cast<TaskActivityType>(task_activity.activitytype);
activity_data->target_name = task_activity.target_name;
activity_data->item_list = task_activity.item_list;
activity_data->skill_list = task_activity.skill_list;
activity_data->skill_id = Strings::IsNumber(task_activity.skill_list) ? std::stoi(task_activity.skill_list) : 0; // for older clients
activity_data->spell_list = task_activity.spell_list;
activity_data->spell_id = Strings::IsNumber(task_activity.spell_list) ? std::stoi(task_activity.spell_list) : 0; // for older clients
activity_data->description_override = task_activity.description_override;
activity_data->npc_id = task_activity.npc_id;
activity_data->npc_goal_id = task_activity.npc_goal_id;
activity_data->npc_match_list = task_activity.npc_match_list;
activity_data->item_id = task_activity.item_id;
activity_data->item_goal_id = task_activity.item_goal_id;
activity_data->item_id_list = task_activity.item_id_list;
activity_data->dz_switch_id = task_activity.dz_switch_id;
activity_data->goal_method = (TaskMethodType) task_activity.goalmethod;
activity_data->goal_count = task_activity.goalcount;
activity_data->min_x = task_activity.min_x;
activity_data->min_y = task_activity.min_y;
activity_data->min_z = task_activity.min_z;
activity_data->max_x = task_activity.max_x;
activity_data->max_y = task_activity.max_y;
activity_data->max_z = task_activity.max_z;
activity_data->zone_version = task_activity.zone_version >= 0 ? task_activity.zone_version : -1;
activity_data->has_area = false;
ad->req_activity_id = a.req_activity_id;
ad->step = step;
ad->activity_type = static_cast<TaskActivityType>(a.activitytype);
ad->target_name = a.target_name;
ad->item_list = a.item_list;
ad->skill_list = a.skill_list;
ad->skill_id = Strings::IsNumber(a.skill_list) ? std::stoi(a.skill_list) : 0; // for older clients
ad->spell_list = a.spell_list;
ad->spell_id = Strings::IsNumber(a.spell_list) ? std::stoi(a.spell_list) : 0; // for older clients
ad->description_override = a.description_override;
ad->npc_match_list = a.npc_match_list;
ad->item_id_list = a.item_id_list;
ad->dz_switch_id = a.dz_switch_id;
ad->goal_method = (TaskMethodType) a.goalmethod;
ad->goal_count = a.goalcount;
ad->min_x = a.min_x;
ad->min_y = a.min_y;
ad->min_z = a.min_z;
ad->max_x = a.max_x;
ad->max_y = a.max_y;
ad->max_z = a.max_z;
ad->zone_version = a.zone_version >= 0 ? a.zone_version : -1;
ad->has_area = false;
if (std::abs(task_activity.max_x - task_activity.min_x) > 0.0f &&
std::abs(task_activity.max_y - task_activity.min_y) > 0.0f &&
std::abs(task_activity.max_z - task_activity.min_z) > 0.0f)
if (std::abs(a.max_x - a.min_x) > 0.0f &&
std::abs(a.max_y - a.min_y) > 0.0f &&
std::abs(a.max_z - a.min_z) > 0.0f)
{
activity_data->has_area = true;
ad->has_area = true;
}
// zones
activity_data->zones = task_activity.zones;
ad->zones = a.zones;
auto zones = Strings::Split(
task_activity.zones,
a.zones,
';'
);
for (auto &&e : zones) {
activity_data->zone_ids.push_back(std::stoi(e));
ad->zone_ids.push_back(std::stoi(e));
}
activity_data->optional = task_activity.optional;
ad->optional = a.optional;
LogTasksDetail(
"[LoadTasks] (Activity) task_id [{}] activity_id [{}] slot [{}] activity_type [{}] goal_method [{}] goal_count [{}] zones [{}]"
@ -256,15 +241,15 @@ bool TaskManager::LoadTasks(int single_task)
task_id,
activity_id,
task_data->activity_count,
static_cast<int32_t>(activity_data->activity_type),
activity_data->goal_method,
activity_data->goal_count,
activity_data->zones.c_str(),
activity_data->target_name.c_str(),
activity_data->item_list.c_str(),
activity_data->skill_list.c_str(),
activity_data->spell_list.c_str(),
activity_data->description_override.c_str()
static_cast<int32_t>(ad->activity_type),
ad->goal_method,
ad->goal_count,
ad->zones.c_str(),
ad->target_name.c_str(),
ad->item_list.c_str(),
ad->skill_list.c_str(),
ad->spell_list.c_str(),
ad->description_override.c_str()
);
task_data->activity_count++;
@ -275,13 +260,13 @@ bool TaskManager::LoadTasks(int single_task)
return true;
}
bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_state)
bool TaskManager::SaveClientState(Client *client, ClientTaskState *cts)
{
// I am saving the slot in the ActiveTasks table, because unless a Task is cancelled/completed, the client
// doesn't seem to like tasks moving slots between zoning and you can end up with 'bogus' activities if the task
// previously in that slot had more activities than the one now occupying it. Hopefully retaining the slot
// number for the duration of a session will overcome this.
if (!client || !client_task_state) {
if (!client || !cts) {
return false;
}
@ -291,10 +276,10 @@ bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_s
LogTasks("[SaveClientState] character_id [{}]", character_id);
if (client_task_state->m_active_task_count > 0 ||
client_task_state->m_active_task.task_id != TASKSLOTEMPTY ||
client_task_state->m_active_shared_task.task_id != TASKSLOTEMPTY) {
for (auto &active_task : client_task_state->m_active_tasks) {
if (cts->m_active_task_count > 0 ||
cts->m_active_task.task_id != TASKSLOTEMPTY ||
cts->m_active_shared_task.task_id != TASKSLOTEMPTY) {
for (auto &active_task : cts->m_active_tasks) {
int task_id = active_task.task_id;
if (task_id == TASKSLOTEMPTY) {
continue;
@ -391,20 +376,20 @@ bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_s
}
}
if (!RuleB(TaskSystem, RecordCompletedTasks) || (client_task_state->m_completed_tasks.size() <=
(unsigned int) client_task_state->m_last_completed_task_loaded)) {
client_task_state->m_last_completed_task_loaded = client_task_state->m_completed_tasks.size();
if (!RuleB(TaskSystem, RecordCompletedTasks) || (cts->m_completed_tasks.size() <=
(unsigned int) cts->m_last_completed_task_loaded)) {
cts->m_last_completed_task_loaded = cts->m_completed_tasks.size();
return true;
}
const char *completed_task_query = "REPLACE INTO completed_tasks (charid, completedtime, taskid, activityid) "
"VALUES (%i, %i, %i, %i)";
for (unsigned int task_index = client_task_state->m_last_completed_task_loaded;
task_index < client_task_state->m_completed_tasks.size();
for (unsigned int task_index = cts->m_last_completed_task_loaded;
task_index < cts->m_completed_tasks.size();
task_index++) {
int task_id = client_task_state->m_completed_tasks[task_index].task_id;
int task_id = cts->m_completed_tasks[task_index].task_id;
const auto task_data = GetTaskData(task_id);
if (!task_data) {
@ -423,7 +408,7 @@ bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_s
std::string query = StringFormat(
completed_task_query,
character_id,
client_task_state->m_completed_tasks[task_index].completed_time,
cts->m_completed_tasks[task_index].completed_time,
task_id,
-1
);
@ -442,14 +427,14 @@ bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_s
// Insert one record for each completed optional task.
for (int activity_id = 0; activity_id < task_data->activity_count; activity_id++) {
if (!task_data->activity_information[activity_id].optional ||
!client_task_state->m_completed_tasks[task_index].activity_done[activity_id]) {
!cts->m_completed_tasks[task_index].activity_done[activity_id]) {
continue;
}
query = StringFormat(
completed_task_query,
character_id,
client_task_state->m_completed_tasks[task_index].completed_time,
cts->m_completed_tasks[task_index].completed_time,
task_id, activity_id
);
@ -460,7 +445,7 @@ bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_s
}
}
client_task_state->m_last_completed_task_loaded = client_task_state->m_completed_tasks.size();
cts->m_last_completed_task_loaded = cts->m_completed_tasks.size();
return true;
}
@ -767,14 +752,14 @@ void TaskManager::SendTaskSelector(Client* client, Mob* mob, const std::vector<i
client->GetTaskState()->ClearLastOffers();
int valid_tasks_count = 0;
for (int task_index = 0; task_index < task_list.size(); task_index++) {
if (!ValidateLevel(task_list[task_index], player_level)) {
for (int task_index : task_list) {
if (!ValidateLevel(task_index, player_level)) {
continue;
}
if (client->IsTaskActive(task_list[task_index])) {
if (client->IsTaskActive(task_index)) {
continue;
}
if (!IsTaskRepeatable(task_list[task_index]) && client->IsTaskCompleted(task_list[task_index])) {
if (!IsTaskRepeatable(task_index) && client->IsTaskCompleted(task_index)) {
continue;
}
@ -828,8 +813,7 @@ void TaskManager::SendSharedTaskSelector(Client* client, Mob* mob, const std::ve
buf.WriteUInt32(static_cast<uint32_t>(TaskType::Shared));
buf.WriteUInt32(mob->GetID()); // task giver entity id
for (int i = 0; i < task_list.size(); ++i) {
int task_id = task_list[i];
for (int task_id: task_list) {
buf.WriteUInt32(task_id);
m_task_data[task_id].SerializeSelector(buf, client->ClientVersion());
client->GetTaskState()->AddOffer(task_id, mob->GetID());
@ -861,22 +845,22 @@ bool TaskManager::IsTaskRepeatable(int task_id)
return task_data->repeatable;
}
void TaskManager::SendCompletedTasksToClient(Client *c, ClientTaskState *client_task_state)
void TaskManager::SendCompletedTasksToClient(Client *c, ClientTaskState *cts)
{
int packet_length = 4;
//vector<CompletedTaskInformation>::const_iterator iterator;
// The client only display the first 50 Completed Tasks send, so send the 50 most recent
int first_task_to_send = 0;
int last_task_to_send = client_task_state->m_completed_tasks.size();
int last_task_to_send = cts->m_completed_tasks.size();
if (client_task_state->m_completed_tasks.size() > 50) {
first_task_to_send = client_task_state->m_completed_tasks.size() - 50;
if (cts->m_completed_tasks.size() > 50) {
first_task_to_send = cts->m_completed_tasks.size() - 50;
}
LogTasks(
"[SendCompletedTasksToClient] completed task count [{}] first tank to send is [{}] last is [{}]",
client_task_state->m_completed_tasks.size(),
cts->m_completed_tasks.size(),
first_task_to_send,
last_task_to_send
);
@ -889,7 +873,7 @@ void TaskManager::SendCompletedTasksToClient(Client *c, ClientTaskState *client_
}
*/
for (int i = first_task_to_send; i < last_task_to_send; i++) {
int task_id = client_task_state->m_completed_tasks[i].task_id;
int task_id = cts->m_completed_tasks[i].task_id;
const auto task_data = GetTaskData(task_id);
if (!task_data) { continue; }
packet_length = packet_length + 8 + task_data->title.size() + 1;
@ -904,7 +888,7 @@ void TaskManager::SendCompletedTasksToClient(Client *c, ClientTaskState *client_
//for(iterator=activity_state->CompletedTasks.begin(); iterator!=activity_state->CompletedTasks.end(); iterator++) {
// int task_id = (*iterator).task_id;
for (int i = first_task_to_send; i < last_task_to_send; i++) {
int task_id = client_task_state->m_completed_tasks[i].task_id;
int task_id = cts->m_completed_tasks[i].task_id;
const auto task_data = GetTaskData(task_id);
if (!task_data) { continue; }
*(uint32 *) buf = task_id;
@ -913,7 +897,7 @@ void TaskManager::SendCompletedTasksToClient(Client *c, ClientTaskState *client_
sprintf(buf, "%s", task_data->title.c_str());
buf = buf + strlen(buf) + 1;
//*(uint32 *)buf = (*iterator).CompletedTime;
*(uint32 *) buf = client_task_state->m_completed_tasks[i].completed_time;
*(uint32 *) buf = cts->m_completed_tasks[i].completed_time;
buf = buf + 4;
}
@ -1130,30 +1114,22 @@ void TaskManager::SendActiveTaskDescription(
bool bring_up_task_journal
)
{
auto task_data = GetTaskData(task_id);
if (!task_data) {
auto t = GetTaskData(task_id);
if (!t) {
return;
}
int packet_length = sizeof(TaskDescriptionHeader_Struct) + task_data->title.length() + 1
+ sizeof(TaskDescriptionData1_Struct) + task_data->description.length() + 1
int packet_length = sizeof(TaskDescriptionHeader_Struct) + t->title.length() + 1
+ sizeof(TaskDescriptionData1_Struct) + t->description.length() + 1
+ sizeof(TaskDescriptionData2_Struct) + 1 + sizeof(TaskDescriptionTrailer_Struct);
// If there is an item make the reward text into a link to the item (only the first item if a list
// is specified). I have been unable to get multiple item links to work.
//
if (task_data->reward_id && task_data->item_link.empty()) {
int item_id = 0;
// If the reward is a list of items, and the first entry on the list is valid
if (task_data->reward_method == METHODSINGLEID) {
item_id = task_data->reward_id;
}
else if (task_data->reward_method == METHODLIST) {
item_id = m_goal_list_manager.GetFirstEntry(task_data->reward_id);
if (item_id < 0) {
item_id = 0;
}
}
if (!t->reward_id_list.empty() && t->item_link.empty()) {
auto items = Strings::Split(t->reward_id_list, "|");
auto item = items.front();
int item_id = Strings::IsNumber(items.front()) ? std::stoi(items.front()) : 0;
if (item_id) {
const EQ::ItemData *reward_item = database.GetItem(item_id);
@ -1162,11 +1138,11 @@ void TaskManager::SendActiveTaskDescription(
linker.SetLinkType(EQ::saylink::SayLinkItemData);
linker.SetItemData(reward_item);
linker.SetTaskUse();
task_data->item_link = linker.GenerateLink();
t->item_link = linker.GenerateLink();
}
}
packet_length += task_data->reward.length() + 1 + task_data->item_link.length() + 1;
packet_length += t->reward.length() + 1 + t->item_link.length() + 1;
char *Ptr;
TaskDescriptionHeader_Struct *task_description_header;
@ -1181,26 +1157,26 @@ void TaskManager::SendActiveTaskDescription(
task_description_header->SequenceNumber = task_info.slot;
task_description_header->TaskID = task_id;
task_description_header->open_window = bring_up_task_journal;
task_description_header->task_type = static_cast<uint32>(task_data->type);
task_description_header->task_type = static_cast<uint32>(t->type);
task_description_header->reward_type = static_cast<int>(task_data->reward_point_type);
task_description_header->reward_type = static_cast<int>(t->reward_point_type);
Ptr = (char *) task_description_header + sizeof(TaskDescriptionHeader_Struct);
sprintf(Ptr, "%s", task_data->title.c_str());
Ptr += task_data->title.length() + 1;
sprintf(Ptr, "%s", t->title.c_str());
Ptr += t->title.length() + 1;
tdd1 = (TaskDescriptionData1_Struct *) Ptr;
tdd1->Duration = duration;
tdd1->dur_code = static_cast<uint32>(task_data->duration_code);
tdd1->dur_code = static_cast<uint32>(t->duration_code);
tdd1->StartTime = start_time;
Ptr = (char *) tdd1 + sizeof(TaskDescriptionData1_Struct);
sprintf(Ptr, "%s", task_data->description.c_str());
Ptr += task_data->description.length() + 1;
sprintf(Ptr, "%s", t->description.c_str());
Ptr += t->description.length() + 1;
tdd2 = (TaskDescriptionData2_Struct *) Ptr;
@ -1209,24 +1185,24 @@ void TaskManager::SendActiveTaskDescription(
// "has_reward_selection" is after this bool! Smaller packet when this is 0
tdd2->has_rewards = 1;
tdd2->coin_reward = task_data->cash_reward;
tdd2->xp_reward = task_data->experience_reward ? 1 : 0; // just booled
tdd2->faction_reward = task_data->faction_reward ? 1 : 0; // faction booled
tdd2->coin_reward = t->cash_reward;
tdd2->xp_reward = t->experience_reward ? 1 : 0; // just booled
tdd2->faction_reward = t->faction_reward ? 1 : 0; // faction booled
Ptr = (char *) tdd2 + sizeof(TaskDescriptionData2_Struct);
// we actually have 2 strings here. One is max length 96 and not parsed for item links
// We actually skipped past that string incorrectly before, so TODO: fix item link string
sprintf(Ptr, "%s", task_data->reward.c_str());
Ptr += task_data->reward.length() + 1;
sprintf(Ptr, "%s", t->reward.c_str());
Ptr += t->reward.length() + 1;
// second string is parsed for item links
sprintf(Ptr, "%s", task_data->item_link.c_str());
Ptr += task_data->item_link.length() + 1;
sprintf(Ptr, "%s", t->item_link.c_str());
Ptr += t->item_link.length() + 1;
tdt = (TaskDescriptionTrailer_Struct *) Ptr;
// shared tasks show radiant/ebon crystal reward, non-shared tasks show generic points
tdt->Points = task_data->reward_points;
tdt->Points = t->reward_points;
tdt->has_reward_selection = 0; // TODO: new rewards window
@ -1234,9 +1210,9 @@ void TaskManager::SendActiveTaskDescription(
safe_delete(outapp);
}
bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_state)
bool TaskManager::LoadClientState(Client *client, ClientTaskState *cts)
{
if (!client || !client_task_state) {
if (!client || !cts) {
return false;
}
@ -1244,7 +1220,7 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
int character_id = client->CharacterID();
client_task_state->m_active_task_count = 0;
cts->m_active_task_count = 0;
LogTasks("[LoadClientState] for character_id [{}]", character_id);
@ -1276,7 +1252,7 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
// client data bucket pointer
// this actually fetches the proper task type instances to be loaded with data
// whether it be quest / task / shared task
auto task_info = client_task_state->GetClientTaskInfo(type, slot);
auto task_info = cts->GetClientTaskInfo(type, slot);
if (task_info == nullptr) {
LogTasks(
"[LoadClientState] Error: slot [{}] out of range while loading character tasks from database",
@ -1301,7 +1277,7 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
// this check keeps a lot of core task updating code from working properly (shared or otherwise)
if (type == TaskType::Quest) {
++client_task_state->m_active_task_count;
++cts->m_active_task_count;
}
LogTasks(
@ -1346,18 +1322,18 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
// type: task
ClientTaskInformation *task_info = nullptr;
if (client_task_state->m_active_task.task_id == task_id) {
task_info = &client_task_state->m_active_task;
if (cts->m_active_task.task_id == task_id) {
task_info = &cts->m_active_task;
}
// type: shared task
if (client_task_state->m_active_shared_task.task_id == task_id) {
task_info = &client_task_state->m_active_shared_task;
if (cts->m_active_shared_task.task_id == task_id) {
task_info = &cts->m_active_shared_task;
}
// type: quest
if (task_info == nullptr) {
for (auto &active_quest : client_task_state->m_active_quests) {
for (auto &active_quest : cts->m_active_quests) {
if (active_quest.task_id == task_id) {
task_info = &active_quest;
}
@ -1396,12 +1372,12 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
);
}
SyncClientSharedTaskState(client, client_task_state);
SyncClientSharedTaskState(client, cts);
if (RuleB(TaskSystem, RecordCompletedTasks)) {
CompletedTaskInformation completed_task_information{};
CompletedTaskInformation cti{};
for (bool &i : completed_task_information.activity_done) {
for (bool &i : cti.activity_done) {
i = false;
}
@ -1434,14 +1410,14 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
int completed_time = character_completed_task.completedtime;
if ((previous_task_id != -1) &&
((task_id != previous_task_id) || (completed_time != previous_completed_time))) {
client_task_state->m_completed_tasks.push_back(completed_task_information);
for (bool &activity_done : completed_task_information.activity_done) {
cts->m_completed_tasks.push_back(cti);
for (bool &activity_done : cti.activity_done) {
activity_done = false;
}
}
completed_task_information.task_id = previous_task_id = task_id;
completed_task_information.completed_time = previous_completed_time = completed_time;
cti.task_id = previous_task_id = task_id;
cti.completed_time = previous_completed_time = completed_time;
// If activity_id is -1, Mark all the non-optional tasks as completed.
if (activity_id < 0) {
@ -1452,20 +1428,20 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
for (int i = 0; i < task_data->activity_count; i++) {
if (!task_data->activity_information[i].optional) {
completed_task_information.activity_done[i] = true;
cti.activity_done[i] = true;
}
}
}
else {
completed_task_information.activity_done[activity_id] = true;
cti.activity_done[activity_id] = true;
}
}
if (previous_task_id != -1) {
client_task_state->m_completed_tasks.push_back(completed_task_information);
cts->m_completed_tasks.push_back(cti);
}
client_task_state->m_last_completed_task_loaded = client_task_state->m_completed_tasks.size();
cts->m_last_completed_task_loaded = cts->m_completed_tasks.size();
}
std::string query = StringFormat(
@ -1479,7 +1455,7 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
if (results.Success()) {
for (auto row = results.begin(); row != results.end(); ++row) {
int task_id = atoi(row[0]);
client_task_state->m_enabled_tasks.push_back(task_id);
cts->m_enabled_tasks.push_back(task_id);
LogTasksDetail("[LoadClientState] Adding task_id [{}] to enabled tasks", task_id);
}
}
@ -1488,7 +1464,7 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
// This should only break if a ServerOP adds or deletes activites for a task that players already
// have active, or due to a bug.
for (int task_index = 0; task_index < MAXACTIVEQUESTS + 1; task_index++) {
int task_id = client_task_state->m_active_tasks[task_index].task_id;
int task_id = cts->m_active_tasks[task_index].task_id;
if (task_id == TASKSLOTEMPTY) {
continue;
}
@ -1503,11 +1479,11 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
);
LogError("[LoadClientState] Character [{}] has task [{}] which does not exist", character_id, task_id);
client_task_state->m_active_tasks[task_index].task_id = TASKSLOTEMPTY;
cts->m_active_tasks[task_index].task_id = TASKSLOTEMPTY;
continue;
}
for (int activity_index = 0; activity_index < task_data->activity_count; activity_index++) {
if (client_task_state->m_active_tasks[task_index].activity[activity_index].activity_id != activity_index) {
if (cts->m_active_tasks[task_index].activity[activity_index].activity_id != activity_index) {
client->Message(
Chat::Red,
"Active Task %i, %s. activity_information count does not match expected value."
@ -1521,7 +1497,7 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
activity_index,
task_id
);
client_task_state->m_active_tasks[task_index].task_id = TASKSLOTEMPTY;
cts->m_active_tasks[task_index].task_id = TASKSLOTEMPTY;
break;
}
}
@ -1529,34 +1505,34 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
LogTasksDetail(
"[LoadClientState] m_active_task task_id is [{}] slot [{}]",
client_task_state->m_active_task.task_id,
client_task_state->m_active_task.slot
cts->m_active_task.task_id,
cts->m_active_task.slot
);
if (client_task_state->m_active_task.task_id != TASKSLOTEMPTY) {
client_task_state->UnlockActivities(client, client_task_state->m_active_task);
if (cts->m_active_task.task_id != TASKSLOTEMPTY) {
cts->UnlockActivities(client, cts->m_active_task);
// purely debugging
LogTasksDetail(
"[LoadClientState] Fetching task info for character_id [{}] task [{}] slot [{}] accepted_time [{}] updated [{}]",
character_id,
client_task_state->m_active_task.task_id,
client_task_state->m_active_task.slot,
client_task_state->m_active_task.accepted_time,
client_task_state->m_active_task.updated
cts->m_active_task.task_id,
cts->m_active_task.slot,
cts->m_active_task.accepted_time,
cts->m_active_task.updated
);
const auto task_data = GetTaskData(client_task_state->m_active_task.task_id);
const auto task_data = GetTaskData(cts->m_active_task.task_id);
if (task_data) {
for (int i = 0; i < task_data->activity_count; i++) {
if (client_task_state->m_active_task.activity[i].activity_id >= 0) {
if (cts->m_active_task.activity[i].activity_id >= 0) {
LogTasksDetail(
"[LoadClientState] -- character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}]",
character_id,
client_task_state->m_active_task.task_id,
client_task_state->m_active_task.activity[i].activity_id,
client_task_state->m_active_task.activity[i].done_count,
client_task_state->m_active_task.activity[i].activity_state,
client_task_state->m_active_task.activity[i].updated
cts->m_active_task.task_id,
cts->m_active_task.activity[i].activity_id,
cts->m_active_task.activity[i].done_count,
cts->m_active_task.activity[i].activity_state,
cts->m_active_task.activity[i].updated
);
}
}
@ -1566,17 +1542,17 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s
// shared task
LogTasksDetail(
"[LoadClientState] m_active_shared_task task_id is [{}] slot [{}]",
client_task_state->m_active_shared_task.task_id,
client_task_state->m_active_shared_task.slot
cts->m_active_shared_task.task_id,
cts->m_active_shared_task.slot
);
if (client_task_state->m_active_shared_task.task_id != TASKSLOTEMPTY) {
client_task_state->UnlockActivities(client, client_task_state->m_active_shared_task);
if (cts->m_active_shared_task.task_id != TASKSLOTEMPTY) {
cts->UnlockActivities(client, cts->m_active_shared_task);
}
// quests (max 20 or 40 depending on client)
for (auto &active_quest : client_task_state->m_active_quests) {
for (auto &active_quest : cts->m_active_quests) {
if (active_quest.task_id != TASKSLOTEMPTY) {
client_task_state->UnlockActivities(client, active_quest);
cts->UnlockActivities(client, active_quest);
}
}

View File

@ -3,7 +3,6 @@
#include "tasks.h"
#include "task_client_state.h"
#include "task_goal_list_manager.h"
#include "../common/types.h"
#include "../common/repositories/character_tasks_repository.h"
#include <list>
@ -20,10 +19,9 @@ class TaskManager {
public:
int GetActivityCount(int task_id);
bool LoadTasks(int single_task = 0);
void ReloadGoalLists();
bool LoadTaskSets();
bool LoadClientState(Client *client, ClientTaskState *client_task_state);
bool SaveClientState(Client *client, ClientTaskState *client_task_state);
bool LoadClientState(Client *client, ClientTaskState *cts);
bool SaveClientState(Client *client, ClientTaskState *cts);
void SendTaskSelector(Client* client, Mob* mob, const std::vector<int>& tasks);
bool ValidateLevel(int task_id, int player_level);
std::string GetTaskName(uint32 task_id);
@ -47,7 +45,7 @@ public:
int client_task_index,
bool task_complete = false
);
void SendCompletedTasksToClient(Client *c, ClientTaskState *client_task_state);
void SendCompletedTasksToClient(Client *c, ClientTaskState *cts);
int FirstTaskInSet(int task_set);
int LastTaskInSet(int task_set);
int NextTaskInSet(int task_set, int task_id);
@ -69,8 +67,7 @@ public:
}
private:
TaskGoalListManager m_goal_list_manager;
std::vector<int> m_task_sets[MAXTASKSETS];
std::vector<int> m_task_sets[MAXTASKSETS];
std::unordered_map<uint32_t, TaskInformation> m_task_data;
void SendActiveTaskDescription(
Client *client,

View File

@ -3465,12 +3465,6 @@ void WorldServer::HandleReloadTasks(ServerPacket *pack)
break;
}
case RELOADTASKGOALLISTS:
{
LogTasks("Global reload of all Task Goal Lists");
task_manager->ReloadGoalLists();
break;
}
case RELOADTASKSETS:
{
LogTasks("Global reload of all Task Sets");