Redesigned the preview system entirely. (#2888)

This commit is contained in:
gir489 2024-03-30 13:41:12 -04:00 committed by GitHub
parent 21da47f3e5
commit 1f556a8c78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 153 additions and 121 deletions

View File

@ -210,4 +210,6 @@ namespace big::functions
using get_ped_seat = CGetPedSeatReturnClass*(*)(PVOID seat_info, CPed* ped); using get_ped_seat = CGetPedSeatReturnClass*(*)(PVOID seat_info, CPed* ped);
using received_clone_remove = void (*)(CNetworkObjectMgr*, CNetGamePlayer*, CNetGamePlayer*, int16_t, uint32_t); using received_clone_remove = void (*)(CNetworkObjectMgr*, CNetGamePlayer*, CNetGamePlayer*, int16_t, uint32_t);
using can_create_vehicle = bool (*)();
} }

View File

@ -365,6 +365,8 @@ namespace big
functions::received_clone_remove m_received_clone_remove; functions::received_clone_remove m_received_clone_remove;
CWeaponInfoManager* m_weapon_info_manager; CWeaponInfoManager* m_weapon_info_manager;
functions::can_create_vehicle m_can_create_vehicle;
}; };
#pragma pack(pop) #pragma pack(pop)
static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned"); static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned");

View File

@ -140,6 +140,8 @@ namespace big
detour_hook_helper::add<hooks::received_clone_remove>("RCR", g_pointers->m_gta.m_received_clone_remove); detour_hook_helper::add<hooks::received_clone_remove>("RCR", g_pointers->m_gta.m_received_clone_remove);
detour_hook_helper::add<hooks::can_create_vehicle>("CCV", g_pointers->m_gta.m_can_create_vehicle);
g_hooking = this; g_hooking = this;
} }

View File

@ -191,6 +191,8 @@ namespace big
static bool sync_reader_serialize_vec3(void* _this, rage::fvector3* vec, float divisor, int size); static bool sync_reader_serialize_vec3(void* _this, rage::fvector3* vec, float divisor, int size);
static bool sync_reader_serialize_vec3_signed(void* _this, rage::fvector3* vec, float divisor, int size); static bool sync_reader_serialize_vec3_signed(void* _this, rage::fvector3* vec, float divisor, int size);
static bool sync_reader_serialize_array(void* _this, void* array, int size); static bool sync_reader_serialize_array(void* _this, void* array, int size);
static bool can_create_vehicle();
}; };
class minhook_keepalive class minhook_keepalive

View File

@ -0,0 +1,10 @@
#include "hooking/hooking.hpp"
#include "gta/pools.hpp"
namespace big
{
bool hooks::can_create_vehicle()
{
return (**g_pointers->m_gta.m_vehicle_pool)->m_item_count < (**g_pointers->m_gta.m_vehicle_pool)->m_size;
}
}

View File

@ -1772,6 +1772,15 @@ namespace big
{ {
g_pointers->m_gta.m_weapon_info_manager = ptr.add(3).rip().sub(72).as<CWeaponInfoManager*>(); g_pointers->m_gta.m_weapon_info_manager = ptr.add(3).rip().sub(72).as<CWeaponInfoManager*>();
} }
},
// Can Create Vehicle
{
"CCV",
"8B 0D ? ? ? ? 39 0D ? ? ? ? 0F 9C C0",
[](memory::handle ptr)
{
g_pointers->m_gta.m_can_create_vehicle = ptr.as<functions::can_create_vehicle>();
}
} }
>(); // don't leave a trailing comma at the end >(); // don't leave a trailing comma at the end

View File

@ -22,107 +22,92 @@ namespace big
void model_preview_service::show_ped(Hash hash) void model_preview_service::show_ped(Hash hash)
{ {
m_ped_clone = 0; if (m_running && m_ped_model_hash != hash)
m_veh_model_hash = 0; {
m_veh_owned_mods.clear(); stop_preview();
return;
}
if (m_ped_model_hash != hash) if (!m_running)
{ {
m_ped_model_hash = hash; m_ped_model_hash = hash;
if (m_ped_model_hash != 0)
{
m_new_model = true;
preview_loop();
}
} }
preview_loop();
} }
void model_preview_service::show_ped(Hash hash, Ped clone) void model_preview_service::show_ped(Hash hash, Ped clone)
{ {
m_veh_model_hash = 0; if (m_running && (m_ped_model_hash != hash || m_ped_clone != clone ))
m_veh_owned_mods.clear(); {
stop_preview();
return;
}
if (m_ped_model_hash != hash || m_ped_clone != clone) if (!m_running)
{ {
m_ped_model_hash = hash; m_ped_model_hash = hash;
m_ped_clone = clone; m_ped_clone = clone;
if (m_ped_model_hash != 0)
{
m_new_model = true;
preview_loop();
}
} }
preview_loop();
} }
void model_preview_service::show_vehicle(Hash hash, bool spawn_max) void model_preview_service::show_vehicle(Hash hash, bool spawn_max)
{ {
m_ped_model_hash = 0; if (m_running && m_veh_model_hash != hash)
m_ped_clone = 0; {
m_veh_owned_mods.clear(); stop_preview();
return;
}
if (m_veh_model_hash != hash || m_veh_spawn_max != spawn_max) if (!m_running)
{ {
m_veh_model_hash = hash; m_veh_model_hash = hash;
m_current_persisted_vehicle_name.clear();
if (m_veh_model_hash != 0)
{
m_veh_spawn_max = spawn_max;
m_new_model = true;
preview_loop();
}
} }
preview_loop();
} }
void model_preview_service::show_vehicle(const std::map<int, int32_t>& owned_mods, bool spawn_max) void model_preview_service::show_vehicle(const std::map<int, int32_t>& owned_mods, bool spawn_max)
{ {
m_ped_model_hash = 0; if (m_running && m_veh_model_hash != owned_mods.find(MOD_MODEL_HASH)->second)
m_ped_clone = 0;
m_current_persisted_vehicle_name.clear();
if (m_veh_spawn_max != spawn_max || m_veh_owned_mods.size() != owned_mods.size()
|| !std::equal(m_veh_owned_mods.begin(), m_veh_owned_mods.end(), owned_mods.begin()))
{ {
m_veh_owned_mods.clear(); stop_preview();
return;
}
if (!m_running)
{
auto hash_item = owned_mods.find(MOD_MODEL_HASH); auto hash_item = owned_mods.find(MOD_MODEL_HASH);
m_veh_model_hash = hash_item->second; m_veh_model_hash = hash_item->second;
m_veh_owned_mods.insert(owned_mods.begin(), owned_mods.end());
if (m_veh_model_hash != 0) m_veh_spawn_max = spawn_max;
{
m_veh_owned_mods.insert(owned_mods.begin(), owned_mods.end());
m_veh_spawn_max = spawn_max;
m_new_model = true;
preview_loop();
}
} }
preview_loop();
} }
void model_preview_service::show_vehicle_persisted(std::string vehicle_name) void model_preview_service::show_vehicle_persisted(std::string vehicle_name)
{ {
m_ped_model_hash = 0; if (m_running && m_current_persisted_vehicle_name != vehicle_name)
m_ped_clone = 0; {
m_veh_model_hash = 0; stop_preview();
return;
if (m_current_persisted_vehicle_name != vehicle_name) }
if (!m_running)
{ {
m_current_persisted_vehicle_name = vehicle_name; m_current_persisted_vehicle_name = vehicle_name;
m_new_model = true;
preview_loop();
} }
preview_loop();
} }
void model_preview_service::preview_loop() void model_preview_service::preview_loop()
{ {
if (m_running || m_loop_running) if (m_running)
{ {
return; return;
} }
@ -130,28 +115,15 @@ namespace big
m_running = true; m_running = true;
g_fiber_pool->queue_job([this] { g_fiber_pool->queue_job([this] {
m_loop_running = true; m_heading = 0.f;
m_heading = 0; m_rotation_start_time = std::chrono::steady_clock::now();
start_time = std::chrono::steady_clock::now();
while (g_running && m_running && g_gui->is_open() && (m_ped_model_hash || m_veh_model_hash || !m_current_persisted_vehicle_name.empty())) while (!m_shutdown_preview && g_running && g_gui->is_open() )
{ {
Vector3 location; Vector3 location{};
if (m_ped_model_hash)
{
location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 5.f, -.5f);
}
else if (m_veh_model_hash || !m_current_persisted_vehicle_name.empty())
{
location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 10.f, .5f);
}
if (m_current_ent == 0) if (m_current_ent == 0)
{ {
m_new_model = false;
location.z = -10.f;
if (m_ped_model_hash) if (m_ped_model_hash)
{ {
m_current_ent = ped::spawn(ePedType::PED_TYPE_ARMY, m_ped_model_hash, m_ped_clone, location, 0.f, false); m_current_ent = ped::spawn(ePedType::PED_TYPE_ARMY, m_ped_model_hash, m_ped_clone, location, 0.f, false);
@ -174,7 +146,7 @@ namespace big
} }
else if (!m_current_persisted_vehicle_name.empty()) else if (!m_current_persisted_vehicle_name.empty())
{ {
m_current_ent = persist_car_service::load_vehicle(m_current_persisted_vehicle_name, g.persist_car.persist_vehicle_sub_folder, Vector3()); m_current_ent = persist_car_service::preview_vehicle(m_current_persisted_vehicle_name, g.persist_car.persist_vehicle_sub_folder, location);
} }
if (m_current_ent) if (m_current_ent)
@ -191,23 +163,28 @@ namespace big
OBJECT::SET_OBJECT_ALLOW_LOW_LOD_BUOYANCY(m_current_ent, false); OBJECT::SET_OBJECT_ALLOW_LOW_LOD_BUOYANCY(m_current_ent, false);
} }
} }
else if (m_new_model)
{
entity::delete_entity(m_current_ent, true);
}
else else
{ {
if (const int alpha = ENTITY::GET_ENTITY_ALPHA(m_current_ent); alpha < 255) if (m_ped_model_hash)
{ {
ENTITY::SET_ENTITY_ALPHA(m_current_ent, std::min<int>(255, alpha + 20), false); location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 5.f, -.5f);
}
else
{
location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 10.f, .5f);
} }
ENTITY::SET_ENTITY_HEADING(m_current_ent, m_heading);
ENTITY::SET_ENTITY_COORDS(m_current_ent, location.x, location.y, location.z, 0, 0, 0, 0);
} }
if (auto alpha = ENTITY::GET_ENTITY_ALPHA(m_current_ent); alpha < 255)
{
ENTITY::SET_ENTITY_ALPHA(m_current_ent, std::min<int>(255, alpha + 20), false);
}
ENTITY::SET_ENTITY_HEADING(m_current_ent, m_heading);
ENTITY::SET_ENTITY_COORDS(m_current_ent, location.x, location.y, location.z, 0, 0, 0, 0);
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time).count() / 1000.0; // Convert to seconds auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_rotation_start_time).count() / 1000.0; // Convert to seconds
m_heading = (elapsed_time / 10.0) * 360.0; // Rotate 360 degrees every 10 seconds m_heading = (elapsed_time / 10.0) * 360.0; // Rotate 360 degrees every 10 seconds
m_heading = fmod(m_heading, 360.0); // Ensure rotation is always between 0 and 360 m_heading = fmod(m_heading, 360.0); // Ensure rotation is always between 0 and 360
@ -215,21 +192,27 @@ namespace big
script::get_current()->yield(); script::get_current()->yield();
} }
entity::delete_entity(m_current_ent, true); ENTITY::DELETE_ENTITY(&m_current_ent);
m_current_ent = 0; clear_data();
m_ped_model_hash = 0;
m_veh_model_hash = 0;
m_veh_owned_mods.clear();
m_current_persisted_vehicle_name.clear();
m_running = false;
m_loop_running = false;
}); });
} }
void model_preview_service::clear_data()
{
m_veh_owned_mods.clear();
m_ped_model_hash = {};
m_veh_model_hash = {};
m_ped_clone = {};
m_current_persisted_vehicle_name = {};
m_shutdown_preview = false;
m_running = false;
m_current_ent = NULL;
}
void model_preview_service::stop_preview() void model_preview_service::stop_preview()
{ {
m_veh_owned_mods.clear(); if (m_running)
m_running = false; m_shutdown_preview = true;
} }
} }

View File

@ -5,24 +5,21 @@ namespace big
{ {
class model_preview_service class model_preview_service
{ {
Entity m_current_ent = 0; Entity m_current_ent{};
float m_heading{};
Hash m_ped_model_hash{};
Hash m_veh_model_hash{};
Ped m_ped_clone{};
std::string m_current_persisted_vehicle_name;
Hash m_veh_model_hash = 0;
std::map<int, int32_t> m_veh_owned_mods; std::map<int, int32_t> m_veh_owned_mods;
bool m_veh_spawn_max = false; bool m_veh_spawn_max = false;
Hash m_ped_model_hash = 0; bool m_running = false;
Ped m_ped_clone = 0; bool m_shutdown_preview = false;
bool m_new_model = false;
float m_heading = 0.f;
bool m_loop_running = false;
bool m_running = false;
std::string m_current_persisted_vehicle_name;
std::chrono::time_point<std::chrono::steady_clock> start_time;
std::chrono::time_point<std::chrono::steady_clock> m_rotation_start_time;
public: public:
model_preview_service(); model_preview_service();
~model_preview_service(); ~model_preview_service();
@ -34,12 +31,11 @@ namespace big
void show_vehicle(const std::map<int, int32_t>& owned_mods, bool spawn_max); void show_vehicle(const std::map<int, int32_t>& owned_mods, bool spawn_max);
void show_vehicle_persisted(std::string vehicle_name); void show_vehicle_persisted(std::string vehicle_name);
void show_vehicle(Vehicle veh); void show_vehicle(Vehicle veh);
void stop_preview();
private: private:
void clear_data();
void preview_loop(); void preview_loop();
public:
void stop_preview();
}; };
inline model_preview_service* g_model_preview_service{}; inline model_preview_service* g_model_preview_service{};

View File

@ -27,6 +27,28 @@ namespace big
file_stream.close(); file_stream.close();
} }
Vehicle persist_car_service::preview_vehicle(std::string_view file_name, std::string folder_name, const std::optional<Vector3>& spawn_coords)
{
const auto file = check_vehicle_folder(folder_name).get_file(file_name);
std::ifstream file_stream(file.get_path());
nlohmann::json vehicle_json;
try
{
file_stream >> vehicle_json;
file_stream.close();
}
catch (std::exception& e)
{
g_notification_service.push_warning("PERSIST_CAR_TITLE"_T.data(), "Failed to load JSON file");
return NULL;
}
return spawn_vehicle_json(vehicle_json, self::ped, spawn_coords, true);
}
Vehicle persist_car_service::load_vehicle(std::string_view file_name, std::string folder_name, const std::optional<Vector3>& spawn_coords) Vehicle persist_car_service::load_vehicle(std::string_view file_name, std::string folder_name, const std::optional<Vector3>& spawn_coords)
{ {
const auto file = check_vehicle_folder(folder_name).get_file(file_name); const auto file = check_vehicle_folder(folder_name).get_file(file_name);
@ -188,16 +210,16 @@ namespace big
return vehicle; return vehicle;
} }
Vehicle persist_car_service::spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped, const std::optional<Vector3>& spawn_coords) Vehicle persist_car_service::spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped, const std::optional<Vector3>& spawn_coords, bool is_preview)
{ {
const Hash vehicle_hash = vehicle_json[vehicle_model_hash_key]; const Hash vehicle_hash = vehicle_json[vehicle_model_hash_key];
const Vector3& spawn_location = spawn_coords.has_value() ? spawn_coords.value() : vehicle::get_spawn_location(g.persist_car.spawn_inside, vehicle_hash); const Vector3& spawn_location = spawn_coords.has_value() ? spawn_coords.value() : vehicle::get_spawn_location(g.persist_car.spawn_inside, vehicle_hash);
const float spawn_heading = ENTITY::GET_ENTITY_HEADING(self::ped); const float spawn_heading = ENTITY::GET_ENTITY_HEADING(self::ped);
Vehicle vehicle = self::veh; Vehicle vehicle = self::veh;
if (spawn_coords.has_value() || (!spawn_coords.has_value() && ENTITY::GET_ENTITY_MODEL(vehicle) != vehicle_hash)) if (is_preview || (!is_preview && ENTITY::GET_ENTITY_MODEL(vehicle) != vehicle_hash))
{ {
vehicle = big::vehicle::spawn(vehicle_hash, spawn_location, spawn_heading); vehicle = big::vehicle::spawn(vehicle_hash, spawn_location, spawn_heading, !is_preview);
if (spawn_location.x + spawn_location.y + spawn_location.z != 0) if (spawn_location.x + spawn_location.y + spawn_location.z != 0)
script::get_current()->yield(); //This is needed to wait for the engine to instantiate things like the radio station so it won't overwrite it on the next frame. script::get_current()->yield(); //This is needed to wait for the engine to instantiate things like the radio station so it won't overwrite it on the next frame.

View File

@ -13,6 +13,7 @@ namespace big
static Vehicle clone_ped_car(Ped ped, Vehicle vehicle); static Vehicle clone_ped_car(Ped ped, Vehicle vehicle);
static void save_vehicle(Vehicle vehicle, std::string_view file_name, std::string folder_name); static void save_vehicle(Vehicle vehicle, std::string_view file_name, std::string folder_name);
static Vehicle load_vehicle(std::string_view file_name, std::string folder_name = "", const std::optional<Vector3>& = std::nullopt); static Vehicle load_vehicle(std::string_view file_name, std::string folder_name = "", const std::optional<Vector3>& = std::nullopt);
static Vehicle preview_vehicle(std::string_view file_name, std::string folder_name = "", const std::optional<Vector3>& = std::nullopt);
static void delete_vehicle(std::string_view file_name, std::string folder_name); static void delete_vehicle(std::string_view file_name, std::string folder_name);
private: private:
@ -68,7 +69,7 @@ namespace big
static Vehicle spawn_vehicle_full(nlohmann::json vehicle_json, Ped ped, const std::optional<Vector3>& spawn_coords = std::nullopt); static Vehicle spawn_vehicle_full(nlohmann::json vehicle_json, Ped ped, const std::optional<Vector3>& spawn_coords = std::nullopt);
static Vehicle spawn_vehicle(nlohmann::json vehicle_json, Ped ped, const std::optional<Vector3>& spawn_coords); static Vehicle spawn_vehicle(nlohmann::json vehicle_json, Ped ped, const std::optional<Vector3>& spawn_coords);
static Vehicle spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped, const std::optional<Vector3>& spawn_coords = std::nullopt); static Vehicle spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped, const std::optional<Vector3>& spawn_coords = std::nullopt, bool is_preview = false);
static nlohmann::json get_full_vehicle_json(Vehicle vehicle); static nlohmann::json get_full_vehicle_json(Vehicle vehicle);

View File

@ -26,7 +26,10 @@ namespace big::entity
void delete_entity(Entity& ent, bool force) void delete_entity(Entity& ent, bool force)
{ {
if (!ENTITY::DOES_ENTITY_EXIST(ent)) if (!ENTITY::DOES_ENTITY_EXIST(ent))
{
ent = NULL;
return; return;
}
if (!force && !take_control_of(ent)) if (!force && !take_control_of(ent))
{ {
LOG(VERBOSE) << "Failed to take control of entity before deleting"; LOG(VERBOSE) << "Failed to take control of entity before deleting";

View File

@ -354,9 +354,9 @@ namespace big::vehicle
} }
Vehicle clone_from_owned_mods(std::map<int, int32_t> owned_mods, Vector3 location, float heading, bool is_networked) Vehicle clone_from_owned_mods(std::map<int, int32_t> owned_mods, Vector3 location, float heading, bool is_networked, bool is_script_vehicle)
{ {
auto vehicle = spawn(owned_mods[MOD_MODEL_HASH], location, heading, is_networked); auto vehicle = spawn(owned_mods[MOD_MODEL_HASH], location, heading, is_networked, is_script_vehicle);
if (vehicle == 0) if (vehicle == 0)
{ {
return 0; return 0;

View File

@ -29,7 +29,7 @@ namespace big::vehicle
Vehicle spawn(Hash hash, Vector3 location, float heading, bool is_networked = true, bool script_veh = false); Vehicle spawn(Hash hash, Vector3 location, float heading, bool is_networked = true, bool script_veh = false);
Vehicle clone_from_vehicle_data(std::map<int, int32_t>& data, Vector3 location, float heading); Vehicle clone_from_vehicle_data(std::map<int, int32_t>& data, Vector3 location, float heading);
std::map<int, int32_t> get_owned_mods_from_vehicle_idx(script_global vehicle_idx); std::map<int, int32_t> get_owned_mods_from_vehicle_idx(script_global vehicle_idx);
Vehicle clone_from_owned_mods(std::map<int, int32_t> owned_mods, Vector3 location, float heading, bool is_networked = true); Vehicle clone_from_owned_mods(std::map<int, int32_t> owned_mods, Vector3 location, float heading, bool is_networked = true, bool is_script_vehicle = false);
std::map<int, int32_t> get_owned_mods_from_vehicle(Vehicle vehicle); std::map<int, int32_t> get_owned_mods_from_vehicle(Vehicle vehicle);
void teleport_into_vehicle(Vehicle veh); void teleport_into_vehicle(Vehicle veh);
void max_vehicle(Vehicle veh); void max_vehicle(Vehicle veh);