esp_manager.cpp
1 // 作者: feng_xingzhe_cn_.86 2 3 #include <onyx/core/esp_manager.hpp> 4 #include <onyx/core/settings.hpp> 5 #include <onyx/sdk/game/GameplayModule.hpp> 6 #include <onyx/sdk/game/Character.hpp> 7 #include <onyx/sdk/game/Skeleton.hpp> 8 #include <onyx/sdk/game/LocalPlayer.hpp> 9 #include <onyx/utils/Memory.hpp> 10 #include <thread> 11 #include <chrono> 12 #include <algorithm> 13 14 namespace onyx::core { 15 16 using namespace sdk::game; 17 18 void EspManager::update_loop(uintptr_t base_address, sdk::game::CameraSystem& camera) noexcept { 19 20 using PlayerEntry = ::onyx::Dictionary<int32_t, uintptr_t>::Entry; 21 22 std::vector<PlayerData> current_batch; 23 std::vector<PlayerEntry> entry_buffer; 24 25 while (true) { 26 27 std::this_thread::sleep_for(std::chrono::milliseconds(4)); // ~240hz 28 29 current_batch.clear(); 30 31 auto gameplay = GameplayModule::instance(); 32 33 if (!gameplay.is_valid()) { 34 clear_players(); 35 continue; 36 } 37 38 camera = gameplay.camera_system; 39 40 if (!camera.is_valid()) { 41 clear_players(); 42 continue; 43 } 44 45 auto game_system = gameplay.game_system; 46 47 if (!game_system.is_valid()) { 48 clear_players(); 49 continue; 50 } 51 52 uintptr_t dict_addr = game_system.player_dict; 53 54 if (!utils::Memory::is_valid_ptr(dict_addr)) { 55 clear_players(); 56 continue; 57 } 58 59 auto dict = utils::Memory::read<::onyx::Dictionary<int32_t, uintptr_t>>(dict_addr); 60 61 uintptr_t entries_addr = reinterpret_cast<uintptr_t>(dict.entries); 62 63 if (!utils::Memory::is_valid_ptr(reinterpret_cast<uintptr_t>(dict.entries)) || dict.count <= 0 || dict.count >= 1000) { 64 clear_players(); 65 continue; 66 } 67 68 current_batch.reserve(dict.count); 69 70 const float screen_w = ImGui::GetIO().DisplaySize.x; 71 const float screen_h = ImGui::GetIO().DisplaySize.y; 72 73 const uintptr_t vector_base = entries_addr + 0x20; 74 75 entry_buffer.resize(dict.count); 76 77 utils::Memory::read_raw(vector_base, entry_buffer.data(), dict.count * sizeof(PlayerEntry)); 78 79 uintptr_t closest_addr = 0; 80 81 float min_dist = 1000000.0f; 82 83 sdk::Vector3 cam_pos = camera.main_camera.position; 84 85 for (int32_t i = 0; i < dict.count; ++i) { 86 87 PlayerEntry& entry = entry_buffer[i]; 88 89 if (entry.hashCode < 0 || !utils::Memory::is_valid_ptr(entry.value)) continue; 90 91 Character character(entry.value); 92 93 float d = sdk::Vector3::Distance(character.transform.position(), cam_pos); 94 95 if (d < min_dist) { 96 97 min_dist = d; 98 closest_addr = entry.value; 99 100 } 101 102 } 103 104 { 105 106 std::lock_guard<std::mutex> lock(m_mutex); 107 108 if (closest_addr != 0) { 109 110 m_local_player = sdk::game::LocalPlayer(closest_addr); 111 112 } 113 114 } 115 116 for (int32_t i = 0; i < dict.count; ++i) { 117 118 PlayerEntry& entry = entry_buffer[i]; 119 120 if (entry.hashCode < 0 || !utils::Memory::is_valid_ptr(entry.value)) { continue; } 121 122 if (entry.value == closest_addr) { continue; } 123 124 Character character(entry.value); 125 126 sdk::Vector3 world_pos = character.transform.position(); 127 128 float d = sdk::Vector3::Distance(world_pos, cam_pos); 129 130 PlayerData& p_data = current_batch.emplace_back(); 131 132 if (camera.world_to_screen(world_pos, p_data.screen_pos, screen_w, screen_h)) { 133 134 p_data.world_pos = world_pos; 135 p_data.health = character.health; 136 p_data.distance = d; 137 p_data.is_visible = true; 138 139 auto skel = Skeleton::find_from_character(character.transform, world_pos); 140 141 if (skel) { 142 143 auto& joints = skel->joints; 144 145 int32_t num = static_cast<int32_t>(joints.size()); 146 147 if (num > k_max_bones) num = k_max_bones; 148 149 p_data.bone_count = num; 150 151 if (num > 0) { 152 153 sdk::Vector3 bmin = joints[0].world_position; 154 sdk::Vector3 bmax = bmin; 155 156 for (int32_t b = 0; b < num; ++b) { 157 158 sdk::Vector3 wp = joints[b].world_position; 159 160 wp.y += Settings::get().esp_skeleton_y_offset; 161 162 if (wp.x < bmin.x) bmin.x = wp.x; 163 if (wp.y < bmin.y) bmin.y = wp.y; 164 if (wp.z < bmin.z) bmin.z = wp.z; 165 if (wp.x > bmax.x) bmax.x = wp.x; 166 if (wp.y > bmax.y) bmax.y = wp.y; 167 if (wp.z > bmax.z) bmax.z = wp.z; 168 169 ImVec2 sp; 170 171 if (camera.world_to_screen(wp, sp, screen_w, screen_h)) { 172 173 p_data.bones[b].screen_pos = sp; 174 p_data.bones[b].parent_index = joints[b].parent_index; 175 p_data.bones[b].is_valid = true; 176 177 } 178 } 179 180 constexpr float k_padding = 0.12f; 181 182 bmin.x -= k_padding; bmin.y -= k_padding; bmin.z -= k_padding; 183 bmax.x += k_padding; bmax.y += k_padding; bmax.z += k_padding; 184 185 p_data.aabb_min = bmin; 186 p_data.aabb_max = bmax; 187 p_data.has_bounds = true; 188 189 } 190 191 } 192 193 } else { 194 195 p_data.world_pos = world_pos; 196 p_data.distance = d; 197 p_data.is_visible = false; 198 p_data.bone_count = 0; 199 p_data.has_bounds = false; 200 201 } 202 203 } 204 205 { 206 207 std::lock_guard<std::mutex> lock(m_mutex); 208 209 m_players = current_batch; 210 211 } 212 213 } 214 215 } 216 217 [[nodiscard]] std::vector<PlayerData> EspManager::players() const noexcept { 218 219 std::lock_guard<std::mutex> lock(m_mutex); 220 221 return m_players; 222 223 } 224 225 void EspManager::clear_players() noexcept { 226 227 std::lock_guard<std::mutex> lock(m_mutex); 228 229 m_players.clear(); 230 231 } 232 233 } // namespace onyx::core