feat(Spawner): Preview vehicle before spawning (#113)

This commit is contained in:
Yimura 2022-03-14 23:31:30 +01:00 committed by GitHub
parent bb8703520a
commit 54bdb6da56
10 changed files with 324 additions and 19 deletions

View File

@ -0,0 +1,30 @@
#pragma once
#include "api/http_request.hpp"
namespace big::remote
{
inline bool download_binary(const std::string_view file_url, const std::filesystem::path& location)
{
std::ofstream file(location, std::ios::binary | std::ios::trunc);
try
{
http::Request req(file_url.data());
http::Response res = req.send("GET", "", {}, 2500ms);
std::ostream_iterator<std::uint8_t> outputIter(file);
std::copy(res.body.begin(), res.body.end(), outputIter);
}
catch (const std::exception& e)
{
LOG(INFO) << "Failed to download binary, is the host down?: " << e.what();
file.close();
return false;
}
file.close();
return true;
}
}

View File

@ -90,6 +90,12 @@ namespace big
hotkeys hotkeys{};
};
struct spawn
{
bool preview_vehicle = false;
bool spawn_inside = false;
};
struct spoofing
{
bool spoof_username = false;
@ -175,6 +181,7 @@ namespace big
protections protections{};
self self{};
settings settings{};
spawn spawn{};
spoofing spoofing{};
vehicle vehicle{};
weapons weapons{};
@ -234,6 +241,9 @@ namespace big
this->settings.hotkeys.menu_toggle = j["settings"]["hotkeys"]["menu_toggle"];
this->spawn.preview_vehicle = j["spawn"]["preview_vehicle"];
this->spawn.spawn_inside = j["spawn"]["spawn_inside"];
this->spoofing.spoof_ip = j["spoofing"]["spoof_ip"];
this->spoofing.spoof_rockstar_id = j["spoofing"]["spoof_rockstar_id"];
this->spoofing.spoof_username = j["spoofing"]["spoof_username"];
@ -355,6 +365,12 @@ namespace big
}
}
},
{
"spawn", {
{ "preview_vehicle", this->spawn.preview_vehicle },
{ "spawn_inside", this->spawn.spawn_inside }
}
},
{
"spoofing", {
{ "spoof_ip", this->spoofing.spoof_ip },

View File

@ -13,5 +13,12 @@ namespace big
static void sub_title(const std::string_view);
static void title(const std::string_view);
static void button(const std::string_view, std::function<void()>);
static void input_text_with_hint(const std::string_view label, const std::string_view hint, char* buf, size_t buf_size, ImGuiInputTextFlags_ flag = ImGuiInputTextFlags_None, std::function<void()> cb = nullptr);
static bool selectable(const std::string_view, bool);
static bool selectable(const std::string_view, bool, ImGuiSelectableFlags);
static void selectable(const std::string_view, bool, std::function<void()>);
static void selectable(const std::string_view, bool, ImGuiSelectableFlags, std::function<void()>);
};
}

View File

@ -0,0 +1,17 @@
#include "gui/components/components.hpp"
#include "fiber_pool.hpp"
#include "natives.hpp"
namespace big
{
void components::input_text_with_hint(const std::string_view label, const std::string_view hint, char* buf, size_t buf_size, ImGuiInputTextFlags_ flag, std::function<void()> cb) {
if (ImGui::InputTextWithHint(label.data(), hint.data(), buf, buf_size, flag))
if (cb)
g_fiber_pool->queue_job(std::move(cb));
if (ImGui::IsItemActive())
g_fiber_pool->queue_job([] {
PAD::DISABLE_ALL_CONTROL_ACTIONS(0);
});
}
}

View File

@ -0,0 +1,29 @@
#include "gui/components/components.hpp"
#include "fiber_pool.hpp"
namespace big
{
bool components::selectable(const std::string_view text, bool selected)
{
return ImGui::Selectable(text.data(), selected);
}
bool components::selectable(const std::string_view text, bool selected, ImGuiSelectableFlags flag)
{
return ImGui::Selectable(text.data(), selected, flag);
}
void components::selectable(const std::string_view text, bool selected, std::function<void()> cb)
{
if (components::selectable(text.data(), selected))
g_fiber_pool->queue_job(std::move(cb));
}
void components::selectable(const std::string_view text, bool selected, ImGuiSelectableFlags flag, std::function<void()> cb)
{
if (components::selectable(text.data(), selected, flag))
{
g_fiber_pool->queue_job(std::move(cb));
}
}
}

View File

@ -15,6 +15,7 @@
#include "services/player_service.hpp"
#include "services/mobile_service.hpp"
#include "services/notification_service.hpp"
#include "services/vehicle_preview_service.hpp"
#include "services/vehicle_service.hpp"
BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
@ -67,6 +68,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
auto mobile_service_instance = std::make_unique<mobile_service>();
auto notification_service_instance = std::make_unique<notification_service>();
auto player_service_instance = std::make_unique<player_service>();
auto vehicle_preview_service_instance = std::make_unique<vehicle_preview_service>();
auto vehicle_service_instance = std::make_unique<vehicle_service>();
LOG(INFO) << "Registered service instances...";
@ -96,6 +98,8 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
vehicle_service_instance.reset();
LOG(INFO) << "Vehicle Service reset.";
vehicle_preview_service_instance.reset();
LOG(INFO) << "Vehicle Preview Service reset.";
mobile_service_instance.reset();
LOG(INFO) << "Mobile Service reset.";
player_service_instance.reset();

View File

@ -0,0 +1,125 @@
#include "api/remote.hpp"
#include "fiber_pool.hpp"
#include "file_manager.hpp"
#include "gui.hpp"
#include "thread_pool.hpp"
#include "util/entity.hpp"
#include "util/vehicle.hpp"
#include "vehicle_preview_service.hpp"
namespace big
{
vehicle_preview_service::vehicle_preview_service() :
m_vehicle_file(g_file_manager->get_project_file("./lib/vehicles.json"))
{
if (m_vehicle_file.exists())
this->load();
else
{
g_thread_pool->push([this]()
{
if (remote::download_binary("http://github-proxy.damon.sh/DurtyFree/gta-v-data-dumps/master/vehicles.json", m_vehicle_file.get_path()))
this->load();
else
LOG(WARNING) << "Failed to download vehicles.json data...";
});
}
g_vehicle_preview_service = this;
}
vehicle_preview_service::~vehicle_preview_service()
{
g_vehicle_preview_service = nullptr;
}
nlohmann::json& vehicle_preview_service::get_vehicle_list()
{
return m_all_vehicles;
}
void vehicle_preview_service::preview_loop()
{
if (m_running)
return;
m_running = true;
static bool busy = false;
while (g_running && g->spawn.preview_vehicle && g_gui.m_opened)
{
if (busy)
continue;
busy = true;
g_fiber_pool->queue_job([this]
{
auto location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(PLAYER::PLAYER_PED_ID(), 2.5f, 2.5f, .5f);
if (m_current_veh == -1)
{
location.z = -10.f;
m_current_veh = vehicle::spawn(m_model, location, 0.f, false);
ENTITY::FREEZE_ENTITY_POSITION(m_current_veh, true);
ENTITY::SET_ENTITY_ALPHA(m_current_veh, 0, 0);
ENTITY::SET_ENTITY_COLLISION(m_current_veh, false, false);
m_new_model = false;
}
else if (m_new_model)
{
entity::delete_entity(m_current_veh);
m_current_veh = -1;
}
else
{
if (const int alpha = ENTITY::GET_ENTITY_ALPHA(m_current_veh); alpha < 250)
ENTITY::SET_ENTITY_ALPHA(m_current_veh, std::min<int>(255, alpha + 10), 0);
ENTITY::SET_ENTITY_HEADING(m_current_veh, m_heading);
ENTITY::SET_ENTITY_COORDS(m_current_veh, location.x, location.y, location.z, 0, 0, 0, 0);
}
if (m_heading += 0.5f; m_heading > 359) m_heading = 0;
busy = false;
});
}
g_fiber_pool->queue_job([this]
{
entity::delete_entity(m_current_veh);
m_current_veh = -1;
});
m_running = false;
}
void vehicle_preview_service::set_preview_vehicle(const nlohmann::json& item)
{
if (m_model != item["Name"])
{
m_model = item["Name"];
m_new_model = true;
}
if (!m_running)
g_thread_pool->push([this] { preview_loop(); });
}
void vehicle_preview_service::load()
{
std::ifstream file(m_vehicle_file.get_path());
try
{
file >> m_all_vehicles;
}
catch (const std::exception& ex)
{
LOG(WARNING) << "Failed to load vehicles.json:\n" << ex.what();
}
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "file_manager/file.hpp"
namespace big
{
class vehicle_preview_service
{
file m_vehicle_file;
std::condition_variable m_cond;
std::mutex m_mutex;
nlohmann::json m_all_vehicles;
Vehicle m_current_veh = -1;
std::string m_model;
bool m_new_model = false;
float m_heading = 0.f;
bool m_running = false;
public:
vehicle_preview_service();
~vehicle_preview_service();
nlohmann::json& get_vehicle_list();
void preview_loop();
void set_preview_vehicle(const nlohmann::json& item);
private:
void load();
};
inline vehicle_preview_service* g_vehicle_preview_service{};
}

View File

@ -44,11 +44,9 @@ namespace big::vehicle
return true;
}
inline int spawn(const char* model, Vector3 location, float heading)
inline int spawn(std::string_view model, Vector3 location, float heading, bool is_networked = true)
{
Hash hash = rage::joaat(model);
if (hash)
if (const Hash hash = rage::joaat(model.data()); hash)
{
for (uint8_t i = 0; !STREAMING::HAS_MODEL_LOADED(hash) && i < 100; i++)
{
@ -64,7 +62,7 @@ namespace big::vehicle
}
*(unsigned short*)g_pointers->m_model_spawn_bypass = 0x9090;
Vehicle veh = VEHICLE::CREATE_VEHICLE(hash, location.x, location.y, location.z, heading, true, false, false);
Vehicle veh = VEHICLE::CREATE_VEHICLE(hash, location.x, location.y, location.z, heading, is_networked, false, false);
*(unsigned short*)g_pointers->m_model_spawn_bypass = 0x0574;
script::get_current()->yield();

View File

@ -1,29 +1,72 @@
#include "views/view.hpp"
#include "fiber_pool.hpp"
#include "natives.hpp"
#include "script.hpp"
#include "services/vehicle_preview_service.hpp"
#include "util/vehicle.hpp"
namespace big
{
static char model[12];
static char model[12] = "";
bool does_search_match(std::string& input, const std::string& search)
{
std::transform(input.begin(), input.end(), input.begin(), ::tolower);
return input.find(search) != std::string::npos;
}
void view::spawn() {
g_fiber_pool->queue_job([] {
PAD::DISABLE_ALL_CONTROL_ACTIONS(0);
});
ImGui::Checkbox("Preview", &g->spawn.preview_vehicle);
ImGui::SameLine();
ImGui::Checkbox("Spawn In", &g->spawn.spawn_inside);
if (
ImGui::InputText("Model Name", model, sizeof(model), ImGuiInputTextFlags_EnterReturnsTrue) ||
ImGui::Button("Spawn")
)
components::input_text_with_hint("Model Name", "Search", model, sizeof(model), ImGuiInputTextFlags_EnterReturnsTrue, []
{
g_fiber_pool->queue_job([] {
Ped player = PLAYER::GET_PLAYER_PED_SCRIPT_INDEX(PLAYER::GET_PLAYER_INDEX());
Vector3 location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(player, .0, 8.0, .5);
const auto location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(PLAYER::PLAYER_PED_ID(), 2.f, 2.f, 0.f);
const Vehicle veh = vehicle::spawn(model, location, g_local_player->m_player_info->m_ped->m_navigation->m_heading + 90.f);
vehicle::spawn((const char*)model, location, ENTITY::GET_ENTITY_HEADING(player) + 90.f);
});
if (g->spawn.spawn_inside)
PED::SET_PED_INTO_VEHICLE(PLAYER::PLAYER_PED_ID(), veh, -1);
});
if (ImGui::ListBoxHeader("###vehicles", { ImGui::GetWindowWidth(), ImGui::GetWindowHeight() }))
{
if (!g_vehicle_preview_service->get_vehicle_list().is_null())
{
for (auto& item : g_vehicle_preview_service->get_vehicle_list())
{
if (item["Name"].is_null() || item["DisplayName"].is_null())
continue;
std::string name = item["Name"];
std::string display_name = item["DisplayName"];
std::string manufacturer;
std::string search = model;
std::transform(search.begin(), search.end(), search.begin(), ::tolower);
if (!item["ManufacturerDisplayName"].is_null())
manufacturer = item["ManufacturerDisplayName"];
if (search.empty() ||
does_search_match(name, search) ||
does_search_match(display_name, search) ||
does_search_match(manufacturer, search))
{
components::selectable(item["DisplayName"], item["Name"] == search, [&item]
{
const auto location = ENTITY::GET_ENTITY_COORDS(PLAYER::PLAYER_PED_ID(), true);
const Vehicle veh = vehicle::spawn(item["Name"], location, 0.f);
if (g->spawn.spawn_inside)
PED::SET_PED_INTO_VEHICLE(PLAYER::PLAYER_PED_ID(), veh, -1);
});
if (g->spawn.preview_vehicle && ImGui::IsItemHovered())
g_vehicle_preview_service->set_preview_vehicle(item);
}
}
}
else ImGui::Text("No vehicles in registry.");
}
}
}