Added ped model data. (#349)

This commit is contained in:
aa15032261 2022-07-19 18:19:19 +08:00 committed by GitHub
parent c9e6ed5db2
commit 18ab39394f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 492 additions and 199 deletions

View File

@ -49,6 +49,8 @@ namespace big::remote
std::string local_etag = "";
std::string remote_etag = "";
const std::vector<std::string> headers = { "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/6.0" };
try {
{
@ -61,7 +63,7 @@ namespace big::remote
if (!local_etag.empty())
{
http::Request req(file_url.data());
http::Response res = req.send("HEAD", "", { "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/6.0" }, 10s);
http::Response res = req.send("HEAD", "", headers, 15s);
remote_etag = get_etag_from_headers(res.headers);
@ -77,7 +79,7 @@ namespace big::remote
}
http::Request req(file_url.data());
http::Response res = req.send("GET", "", {}, 10s);
http::Response res = req.send("GET", "", headers, 30s);
std::ofstream file_ofstream(file_location, std::ios::binary | std::ios::trunc);
std::ostream_iterator<std::uint8_t> file_out_iter(file_ofstream);

View File

@ -14,6 +14,7 @@
#include "services/context_menu/context_menu_service.hpp"
#include "services/globals/globals_service.hpp"
#include "services/gui/gui_service.hpp"
#include "services/gta_data/gta_data_service.hpp"
#include "services/mobile/mobile_service.hpp"
#include "services/pickups/pickup_service.hpp"
#include "services/players/player_service.hpp"
@ -76,6 +77,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
auto notification_service_instance = std::make_unique<notification_service>();
auto pickup_service_instance = std::make_unique<pickup_service>();
auto player_service_instance = std::make_unique<player_service>();
auto gta_data_service_instance = std::make_unique<gta_data_service>();
auto vehicle_preview_service_instance = std::make_unique<vehicle_preview_service>();
auto vehicle_service_instance = std::make_unique<vehicle_service>();
auto gui_service_instance = std::make_unique<gui_service>();
@ -120,6 +122,8 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
gui_service_instance.reset();
LOG(INFO) << "Gui Service reset.";
gta_data_service_instance.reset();
LOG(INFO) << "GTA Data Service reset.";
vehicle_service_instance.reset();
LOG(INFO) << "Vehicle Service reset.";
vehicle_preview_service_instance.reset();

View File

@ -0,0 +1,220 @@
#include "api/remote.hpp"
#include "file_manager.hpp"
#include "thread_pool.hpp"
#include "gta_data_service.hpp"
namespace big
{
gta_data_service::gta_data_service() :
m_vehicle_file(g_file_manager->get_project_file("./lib/vehicles.json")),
m_vehicle_file_etag(g_file_manager->get_project_file("./lib/vehicles_etag.txt")),
m_ped_file(g_file_manager->get_project_file("./lib/peds.json")),
m_ped_file_etag(g_file_manager->get_project_file("./lib/peds_etag.txt"))
{
load_from_file(
m_vehicle_file,
m_vehicle_file_etag,
"http://github-proxy.damon.sh/DurtyFree/gta-v-data-dumps/master/vehicles.json",
&gta_data_service::load_vehicles,
"Vehicle"
);
load_from_file(
m_ped_file,
m_ped_file_etag,
"http://github-proxy.damon.sh/DurtyFree/gta-v-data-dumps/master/peds.json",
&gta_data_service::load_ped,
"Ped"
);
g_gta_data_service = this;
}
gta_data_service::~gta_data_service()
{
g_gta_data_service = nullptr;
}
const vehicle_item& gta_data_service::find_vehicle_by_hash(Hash hash)
{
int idx = -1;
if (m_vehicle_hash_idx_map.count(hash))
{
idx = m_vehicle_hash_idx_map[hash];
}
if (idx == -1)
{
return empty_vehicle_item;
}
else
{
return m_vehicle_item_arr[idx];
}
}
std::vector<std::string>& gta_data_service::get_vehicle_class_arr()
{
return m_vehicle_class_arr;
}
std::vector<vehicle_item>& gta_data_service::get_vehicle_arr()
{
return m_vehicle_item_arr;
}
const ped_item& gta_data_service::find_ped_by_hash(Hash hash)
{
int idx = -1;
if (m_ped_hash_idx_map.count(hash))
{
idx = m_ped_hash_idx_map[hash];
}
if (idx == -1)
{
return empty_ped_item;
}
else
{
return m_ped_item_arr[idx];
}
}
std::vector<std::string>& gta_data_service::get_ped_type_arr()
{
return m_ped_type_arr;
}
std::vector<ped_item>& gta_data_service::get_ped_arr()
{
return m_ped_item_arr;
}
void gta_data_service::load_from_file(file file_to_load, file file_etag, std::string url, void(gta_data_service::* load_func)(), std::string data_name)
{
if (file_to_load.exists())
{
(this->*load_func)();
LOG(INFO) << "Data loaded: " + data_name;
}
g_thread_pool->push([this, file_to_load, file_etag, url, load_func, data_name]() {
for (int retry = 0; retry < 2; retry++)
{
bool ret = remote::update_binary(
url,
file_to_load.get_path(),
file_etag.get_path()
);
if (ret)
{
(this->*load_func)();
LOG(INFO) << "Data updated: " + data_name;
break;
}
else if (!m_vehicle_file.exists())
{
LOG(WARNING) << "Failed to download data: " + data_name;
}
}
});
}
void gta_data_service::load_vehicles()
{
m_vehicle_class_arr.clear();
m_vehicle_hash_idx_map.clear();
m_vehicle_item_arr.clear();
std::ifstream file(m_vehicle_file.get_path());
nlohmann::json all_vehicles;
try
{
file >> all_vehicles;
}
catch (const std::exception& ex)
{
LOG(WARNING) << "Failed to load vehicles.json:\n" << ex.what();
}
for (auto& item_json : all_vehicles)
{
if (
item_json["Hash"].is_null() ||
item_json["Name"].is_null() ||
!item_json["Bones"].is_array() ||
item_json["Bones"][0] == "stub"
)
{
continue;
}
auto item = vehicle_item(item_json);
m_vehicle_hash_idx_map[item_json["Hash"]] = (int)m_vehicle_item_arr.size();
m_vehicle_item_arr.push_back(item);
if (std::find(m_vehicle_class_arr.begin(), m_vehicle_class_arr.end(), item.clazz) == m_vehicle_class_arr.end())
{
m_vehicle_class_arr.push_back(item.clazz);
}
std::sort(m_vehicle_class_arr.begin(), m_vehicle_class_arr.end());
}
}
void gta_data_service::load_ped()
{
m_ped_type_arr.clear();
m_ped_hash_idx_map.clear();
m_ped_item_arr.clear();
std::ifstream file(m_ped_file.get_path());
nlohmann::json all_peds;
try
{
file >> all_peds;
}
catch (const std::exception& ex)
{
LOG(WARNING) << "Failed to load peds.json:\n" << ex.what();
}
for (auto& item_json : all_peds)
{
if (
item_json["Hash"].is_null() ||
item_json["Name"].is_null()
)
{
continue;
}
auto item = ped_item(item_json);
m_ped_hash_idx_map[item_json["Hash"]] = (int)m_ped_item_arr.size();
m_ped_item_arr.push_back(item);
if (std::find(m_ped_type_arr.begin(), m_ped_type_arr.end(), item.ped_type) == m_ped_type_arr.end())
{
m_ped_type_arr.push_back(item.ped_type);
}
std::sort(m_ped_type_arr.begin(), m_ped_type_arr.end());
}
}
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "file_manager/file.hpp"
#include "vehicle_item.hpp"
#include "ped_item.hpp"
namespace big
{
class gta_data_service
{
file m_vehicle_file;
file m_vehicle_file_etag;
file m_ped_file;
file m_ped_file_etag;
std::vector<std::string> m_vehicle_class_arr;
std::map<Hash, int> m_vehicle_hash_idx_map;
std::vector<vehicle_item> m_vehicle_item_arr;
const vehicle_item empty_vehicle_item = vehicle_item();
std::vector<std::string> m_ped_type_arr;
std::map<Hash, int> m_ped_hash_idx_map;
std::vector<ped_item> m_ped_item_arr;
const ped_item empty_ped_item = ped_item();
public:
gta_data_service();
~gta_data_service();
const vehicle_item& find_vehicle_by_hash(Hash hash);
std::vector<std::string>& get_vehicle_class_arr();
std::vector<vehicle_item>& get_vehicle_arr();
const ped_item& find_ped_by_hash(Hash hash);
std::vector<std::string>& get_ped_type_arr();
std::vector<ped_item>& get_ped_arr();
private:
void load_from_file(file file_to_load, file file_etag, std::string url, void(gta_data_service::* load_func)(), std::string data_name);
void load_vehicles();
void load_ped();
};
inline gta_data_service* g_gta_data_service{};
}

View File

@ -0,0 +1,20 @@
#include "ped_item.hpp"
namespace big
{
ped_item::ped_item()
{
this->name = "";
this->ped_type = "";
this->hash = 0;
}
ped_item::ped_item(nlohmann::json& item_json)
{
this->name = item_json["Name"];
this->ped_type = item_json["Pedtype"];
std::transform(this->ped_type.begin(), this->ped_type.end(), this->ped_type.begin(), ::toupper);
this->hash = item_json["Hash"];
}
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "file_manager/file.hpp"
namespace big
{
class ped_item {
public:
ped_item();
ped_item(nlohmann::json& item_json);
std::string name;
std::string ped_type;
Hash hash;
};
}

View File

@ -0,0 +1,46 @@
#include "vehicle_item.hpp"
namespace big
{
vehicle_item::vehicle_item()
{
this->name = "";
this->display_name = "";
this->display_manufacturer = "";
this->clazz = "";
this->hash = 0;
}
vehicle_item::vehicle_item(nlohmann::json& item_json)
{
this->name = item_json["Name"];
this->display_name = item_json["Name"];
this->display_manufacturer = "";
this->clazz = "";
this->hash = item_json["Hash"];
if (!item_json["DisplayName"].is_null())
{
this->display_name = item_json["DisplayName"];
}
if (!item_json["ManufacturerDisplayName"].is_null())
{
this->display_manufacturer = item_json["ManufacturerDisplayName"];
}
else if (!item_json["Manufacturer"].is_null())
{
this->display_manufacturer = item_json["Manufacturer"];
}
if (!item_json["Class"].is_null())
{
this->clazz = item_json["Class"];
std::transform(this->clazz.begin(), this->clazz.end(), this->clazz.begin(), ::toupper);
if (this->clazz == "COMPACTS")
{
this->clazz = "COMPACT";
}
}
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "file_manager/file.hpp"
namespace big
{
class vehicle_item {
public:
vehicle_item();
vehicle_item(nlohmann::json& item_json);
std::string name;
std::string display_name;
std::string display_manufacturer;
std::string clazz;
Hash hash;
};
}

View File

@ -1,6 +1,4 @@
#include "api/remote.hpp"
#include "fiber_pool.hpp"
#include "file_manager.hpp"
#include "gui.hpp"
#include "thread_pool.hpp"
#include "util/entity.hpp"
@ -9,76 +7,8 @@
namespace big
{
vehicle_preview_item::vehicle_preview_item()
vehicle_preview_service::vehicle_preview_service()
{
this->name = "";
this->display_name = "";
this->display_manufacturer = "";
this->clazz = "";
this->hash = 0;
}
vehicle_preview_item::vehicle_preview_item(nlohmann::json& item_json)
{
this->name = item_json["Name"];
this->display_name = item_json["Name"];
this->display_manufacturer = "";
this->clazz = "";
this->hash = item_json["Hash"];
if (!item_json["DisplayName"].is_null())
{
this->display_name = item_json["DisplayName"];
}
if (!item_json["ManufacturerDisplayName"].is_null())
{
this->display_manufacturer = item_json["ManufacturerDisplayName"];
}
else if (!item_json["Manufacturer"].is_null())
{
this->display_manufacturer = item_json["Manufacturer"];
}
if (!item_json["Class"].is_null())
{
this->clazz = item_json["Class"];
if (this->clazz == "COMPACTS")
{
this->clazz = "COMPACT";
}
}
}
vehicle_preview_service::vehicle_preview_service() :
m_vehicle_file(g_file_manager->get_project_file("./lib/vehicles.json")),
m_vehicle_file_etag(g_file_manager->get_project_file("./lib/vehicles_etag.txt"))
{
if (m_vehicle_file.exists())
{
this->load();
LOG(INFO) << "Vehicle data loaded.";
}
g_thread_pool->push([this]() {
bool ret = remote::update_binary(
"http://github-proxy.damon.sh/DurtyFree/gta-v-data-dumps/master/vehicles.json",
m_vehicle_file.get_path(),
m_vehicle_file_etag.get_path()
);
if (ret)
{
this->load();
LOG(INFO) << "Vehicle data updated.";
}
else if (!m_vehicle_file.exists())
{
LOG(WARNING) << "Failed to download vehicles.json data...";
}
});
g_vehicle_preview_service = this;
}
@ -87,36 +17,7 @@ namespace big
g_vehicle_preview_service = nullptr;
}
const vehicle_preview_item& vehicle_preview_service::find_vehicle_item_by_hash(Hash hash)
{
int idx = -1;
if (m_hash_idx_map.count(hash))
{
idx = m_hash_idx_map[hash];
}
if (idx == -1)
{
return empty_item;
}
else
{
return m_vehicle_preview_item_arr[idx];
}
}
std::vector<std::string>& vehicle_preview_service::get_vehicle_class_arr()
{
return m_vehicle_class_arr;
}
std::vector<vehicle_preview_item>& vehicle_preview_service::get_vehicle_preview_item_arr()
{
return m_vehicle_preview_item_arr;
}
void vehicle_preview_service::set_preview_vehicle(const vehicle_preview_item& item)
void vehicle_preview_service::set_preview_vehicle(const vehicle_item& item)
{
if (item.hash != 0)
{
@ -188,49 +89,4 @@ namespace big
{
m_running = false;
}
void vehicle_preview_service::load()
{
m_hash_idx_map.clear();
m_vehicle_preview_item_arr.clear();
std::ifstream file(m_vehicle_file.get_path());
nlohmann::json all_vehicles;
try
{
file >> all_vehicles;
}
catch (const std::exception& ex)
{
LOG(WARNING) << "Failed to load vehicles.json:\n" << ex.what();
}
for (auto& item_json : all_vehicles)
{
if (
item_json["Hash"].is_null() ||
item_json["Name"].is_null() ||
!item_json["Bones"].is_array() ||
item_json["Bones"][0] == "stub"
)
{
continue;
}
auto item = vehicle_preview_item(item_json);
m_hash_idx_map[item_json["Hash"]] = (int)m_vehicle_preview_item_arr.size();
m_vehicle_preview_item_arr.push_back(item);
if (std::find(m_vehicle_class_arr.begin(), m_vehicle_class_arr.end(), item.clazz) == m_vehicle_class_arr.end())
{
m_vehicle_class_arr.push_back(item.clazz);
}
std::sort(m_vehicle_class_arr.begin(), m_vehicle_class_arr.end());
}
}
}

View File

@ -1,34 +1,14 @@
#pragma once
#include "file_manager/file.hpp"
#include "services/gta_data/gta_data_service.hpp"
namespace big
{
class vehicle_preview_item {
public:
vehicle_preview_item();
vehicle_preview_item(nlohmann::json& item_json);
std::string name;
std::string display_name;
std::string display_manufacturer;
std::string clazz;
Hash hash;
};
class vehicle_preview_service
{
file m_vehicle_file;
file m_vehicle_file_etag;
std::condition_variable m_cond;
std::mutex m_mutex;
std::map<Hash, int> m_hash_idx_map;
std::vector<std::string> m_vehicle_class_arr;
std::vector<vehicle_preview_item> m_vehicle_preview_item_arr;
const vehicle_preview_item empty_item = vehicle_preview_item();
Vehicle m_current_veh = -1;
Hash m_model_hash;
bool m_new_model = false;
@ -38,17 +18,10 @@ namespace big
vehicle_preview_service();
~vehicle_preview_service();
const vehicle_preview_item& find_vehicle_item_by_hash(Hash hash);
std::vector<std::string>& get_vehicle_class_arr();
std::vector<vehicle_preview_item>& get_vehicle_preview_item_arr();
void set_preview_vehicle(const vehicle_preview_item& item);
void set_preview_vehicle(const vehicle_item& item);
void preview_loop();
void stop_preview();
private:
void load();
};
inline vehicle_preview_service* g_vehicle_preview_service{};

View File

@ -2,6 +2,8 @@
#include "util/entity.hpp"
#include "util/player.hpp"
#include "views/view.hpp"
#include "services/gta_data/gta_data_service.hpp"
#include <imgui_internal.h>
namespace big
{
@ -38,29 +40,126 @@ namespace big
CUTSCENE::STOP_CUTSCENE_IMMEDIATELY();
});
static char model[32];
components::input_text_with_hint("Model Name###player_ped_model", "Player Model Name", model, sizeof(model), ImGuiInputTextFlags_EnterReturnsTrue, [] {
const Hash hash = rage::joaat(model);
ImGui::Separator();
components::small_text("Player Model Changer");
static int selected_player_ped_type = -1;
static bool player_model_open = false;
static char player_model_name[64];
auto ped_type_arr = g_gta_data_service->get_ped_type_arr();
auto ped_arr = g_gta_data_service->get_ped_arr();
ImGui::SetNextItemWidth(300.f);
if (ImGui::BeginCombo("Ped Type", selected_player_ped_type == -1 ? "ALL" : ped_type_arr[selected_player_ped_type].c_str()))
{
if (ImGui::Selectable("ALL", selected_player_ped_type == -1))
{
selected_player_ped_type = -1;
}
for (int i = 0; i < ped_type_arr.size(); i++)
{
if (ImGui::Selectable(ped_type_arr[i].c_str(), selected_player_ped_type == i))
{
selected_player_ped_type = i;
player_model_name[0] = 0;
}
if (selected_player_ped_type == i)
{
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
ImGui::SetNextItemWidth(300.f);
components::input_text_with_hint("Model Name##player_model_name", "Model Name", player_model_name, sizeof(player_model_name), ImGuiInputTextFlags_EnterReturnsTrue, [] {
player_model_open = false;
});
bool player_model_focused = ImGui::IsItemActive();
if (ImGui::IsItemActivated())
{
player_model_open = true;
}
if (player_model_open)
{
bool is_open = true;
std::string lower_search = player_model_name;
std::transform(lower_search.begin(), lower_search.end(), lower_search.begin(), tolower);
ImGui::SetNextWindowPos({ ImGui::GetItemRectMin().x, ImGui::GetItemRectMax().y });
ImGui::SetNextWindowSize({ 300, 300 });
if (ImGui::Begin("##player_model_popup", &is_open, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_Tooltip))
{
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
player_model_focused |= ImGui::IsWindowFocused();
for (auto& item : ped_arr)
{
std::string ped_type = item.ped_type;
std::string name = item.name;
std::transform(name.begin(), name.end(), name.begin(), tolower);
if ((
selected_player_ped_type == -1 || ped_type_arr[selected_player_ped_type] == ped_type
) && (
name.find(lower_search) != std::string::npos
)) {
bool selectable_highlighted = lower_search == name;
bool selectable_clicked = ImGui::Selectable(item.name.c_str(), selectable_highlighted);
player_model_focused |= ImGui::IsItemFocused();
if (selectable_clicked)
{
strncpy(player_model_name, item.name.c_str(), 64);
player_model_open = false;
player_model_focused = false;
}
if (selectable_highlighted)
{
ImGui::SetItemDefaultFocus();
}
}
}
ImGui::End();
}
player_model_open = player_model_focused;
}
components::button("Change Player Model", [] {
const Hash hash = rage::joaat(player_model_name);
for (uint8_t i = 0; !STREAMING::HAS_MODEL_LOADED(hash) && i < 100; i++)
{
STREAMING::REQUEST_MODEL(hash);
script::get_current()->yield();
}
if (!STREAMING::HAS_MODEL_LOADED(hash))
{
g_notification_service->push_error("Self", "Failed to spawn model, did you give an incorrect model ? ");
return;
}
PLAYER::SET_PLAYER_MODEL(PLAYER::GET_PLAYER_INDEX(), hash);
PED::SET_PED_DEFAULT_COMPONENT_VARIATION(self::ped);
script::get_current()->yield();
STREAMING::SET_MODEL_AS_NO_LONGER_NEEDED(hash);
});
ImGui::Separator();
components::small_text("General");

View File

@ -44,7 +44,7 @@ namespace big
static int selected_class = -1;
auto class_arr = g_vehicle_preview_service->get_vehicle_class_arr();
auto class_arr = g_gta_data_service->get_vehicle_class_arr();
ImGui::SetNextItemWidth(300.f);
if (ImGui::BeginCombo("Vehicle Class", selected_class == -1 ? "ALL" : class_arr[selected_class].c_str()))
@ -72,29 +72,27 @@ namespace big
static char search[64];
static std::string lower_search;
ImGui::SetNextItemWidth(300.f);
components::input_text_with_hint("Model Name", "Search", search, sizeof(search), ImGuiInputTextFlags_None, [] {
lower_search = search;
std::transform(lower_search.begin(), lower_search.end(), lower_search.begin(), tolower);
});
components::input_text_with_hint("Model Name", "Search", search, sizeof(search), ImGuiInputTextFlags_None);
g_mobile_service->refresh_personal_vehicles();
if (ImGui::ListBoxHeader("###personal_veh_list", { 300, static_cast<float>(*g_pointers->m_resolution_y - 184 - 38 * num_of_rows) }))
{
if (g_mobile_service->personal_vehicles().empty())
{
ImGui::Text("No personal vehicles found, \nare you online?");
}
else
{
std::string lower_search = search;
std::transform(lower_search.begin(), lower_search.end(), lower_search.begin(), tolower);
for (const auto& it : g_mobile_service->personal_vehicles())
{
const auto& label = it.first;
const auto& personal_veh = it.second;
auto item = g_vehicle_preview_service->find_vehicle_item_by_hash(personal_veh->get_hash());
auto item = g_gta_data_service->find_vehicle_by_hash(personal_veh->get_hash());
std::string clazz = item.clazz;
std::string display_name = label;
@ -153,7 +151,6 @@ namespace big
else
{
strcpy(search, "");
lower_search = search;
personal_veh->summon();
}

View File

@ -26,7 +26,7 @@ namespace big
static int selected_class = -1;
auto class_arr = g_vehicle_preview_service->get_vehicle_class_arr();
auto class_arr = g_gta_data_service->get_vehicle_class_arr();
ImGui::SetNextItemWidth(300.f);
if (ImGui::BeginCombo("Vehicle Class", selected_class == -1 ? "ALL" : class_arr[selected_class].c_str()))
@ -54,22 +54,20 @@ namespace big
static char search[64];
static std::string lower_search;
ImGui::SetNextItemWidth(300.f);
components::input_text_with_hint("Model Name", "Search", search, sizeof(search), ImGuiInputTextFlags_None, [] {
lower_search = search;
std::transform(lower_search.begin(), lower_search.end(), lower_search.begin(), tolower);
});
components::input_text_with_hint("Model Name", "Search", search, sizeof(search), ImGuiInputTextFlags_None);
// arbitrary subtraction this looked nice so idc, works for all resolutions as well
if (ImGui::ListBoxHeader("###vehicles", { 300, static_cast<float>(*g_pointers->m_resolution_y - 184 - 38 * 4) }))
{
auto item_arr = g_vehicle_preview_service->get_vehicle_preview_item_arr();
auto item_arr = g_gta_data_service->get_vehicle_arr();
if (item_arr.size() > 0)
{
std::string lower_search = search;
std::transform(lower_search.begin(), lower_search.end(), lower_search.begin(), tolower);
for (auto& item : item_arr) {
std::string display_name = item.display_name;